Merge branch 'dev' of gitea.51mars.com:Oak-Team/oak-pay-business into dev

This commit is contained in:
lxy 2024-07-04 17:28:45 +08:00
commit 849c4179e4
50 changed files with 498 additions and 139 deletions

View File

@ -2,6 +2,7 @@
* EntityDict的重新声明
* by Xc 20230807
*/
import React from 'react';
import { EntityDict } from '../oak-app-domain';
import { ReactComponentProps, ColumnProps, RowWithActions, OakExtraActionProps, OakAbsAttrDef, onActionFnDef, ListButtonProps, OakAbsAttrUpsertDef } from 'oak-frontend-base';
declare const FilterPanel: <T extends keyof EntityDict>(props: ReactComponentProps<EntityDict, T, false, {

View File

@ -2,7 +2,6 @@
* 抽象组件在业务层根据EntityDict的重新声明
* by Xc 20230807
*/
// @ts-nocheck
import AbsFilterPanel from 'oak-frontend-base/es/components/filterPanel';
import AbsList from 'oak-frontend-base/es/components/list';
import AbsListPro from 'oak-frontend-base/es/components/listPro';

View File

@ -1,4 +1,4 @@
import { String, Text, Price } from 'oak-domain/lib/types/DataType';
import { String, Text, Price, Datetime } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc, ActionDef } from 'oak-domain/lib/types';
import { Schema as User } from './User';
@ -14,6 +14,7 @@ export interface Schema extends EntityShape {
price: Price;
creator: User;
reason?: Text;
successAt?: Datetime;
opers: AccountOper[];
}
export type IAction = 'succeed' | 'fail';

View File

@ -20,7 +20,8 @@ export const entityDesc = {
iState: '状态',
creator: '创建者',
reason: '原因',
opers: '相关账户操作'
opers: '相关账户操作',
successAt: '退款成功时间'
},
action: {
succeed: '退款成功',

View File

@ -3,7 +3,7 @@ import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, MakeFil
import { OneOf } from "oak-domain/lib/types/Polyfill";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, AggregationResult, EntityShape } from "oak-domain/lib/types/Entity";
import { Action, ParticularAction, IState } from "./Action";
import { Price, String, Text } from "oak-domain/lib/types/DataType";
import { Price, String, Text, Datetime } from "oak-domain/lib/types/DataType";
import * as Withdraw from "../Withdraw/Schema";
import * as Pay from "../Pay/Schema";
import * as User from "../User/Schema";
@ -18,6 +18,7 @@ export type OpSchema = EntityShape & {
price: Price;
creatorId: ForeignKey<"user">;
reason?: Text | null;
successAt?: Datetime | null;
iState?: IState | null;
};
export type OpAttr = keyof OpSchema;
@ -30,6 +31,7 @@ export type Schema = EntityShape & {
price: Price;
creatorId: ForeignKey<"user">;
reason?: Text | null;
successAt?: Datetime | null;
iState?: IState | null;
withdraw?: Withdraw.Schema | null;
pay: Pay.Schema;
@ -57,6 +59,7 @@ type AttrFilter = {
creatorId: Q_StringValue;
creator: User.Filter;
reason: Q_StringValue;
successAt: Q_DateValue;
iState: Q_EnumValue<IState>;
sysAccountOper$refund: SysAccountOper.Filter & SubQueryPredicateMetadata;
accountOper$entity: AccountOper.Filter & SubQueryPredicateMetadata;
@ -80,6 +83,7 @@ export type Projection = {
creatorId?: number;
creator?: User.Projection;
reason?: number;
successAt?: number;
iState?: number;
sysAccountOper$refund?: SysAccountOper.Selection & {
$entity: "sysAccountOper";
@ -134,6 +138,8 @@ export type SortAttr = {
creator: User.SortAttr;
} | {
reason: number;
} | {
successAt: number;
} | {
iState: number;
} | {

View File

@ -35,6 +35,9 @@ export const desc = {
reason: {
type: "text"
},
successAt: {
type: "datetime"
},
iState: {
type: "enum",
enumeration: ["refunding", "successful", "failed"]

View File

@ -1 +1 @@
{ "name": "帐户", "attr": { "pay": "关联支付", "price": "价格", "loss": "损耗", "withdraw": "关联提现", "meta": "metadata", "externalId": "外部退款流水号", "iState": "状态", "creator": "创建者", "reason": "原因", "opers": "相关账户操作" }, "action": { "succeed": "退款成功", "fail": "退款失败" }, "v": { "iState": { "refunding": "退款中", "successful": "退款成功", "failed": "退款失败" } } }
{ "name": "帐户", "attr": { "pay": "关联支付", "price": "价格", "loss": "损耗", "withdraw": "关联提现", "meta": "metadata", "externalId": "外部退款流水号", "iState": "状态", "creator": "创建者", "reason": "原因", "opers": "相关账户操作", "successAt": "退款成功时间" }, "action": { "succeed": "退款成功", "fail": "退款失败" }, "v": { "iState": { "refunding": "退款中", "successful": "退款成功", "failed": "退款失败" } } }

View File

@ -322,7 +322,7 @@ const triggers = [
return;
}
}
const data = await payClazz.refund(refund);
const data = await payClazz.refund(refund, context);
if (data) {
assert(data.externalId);
const closeFn = context.openRootMode();

View File

@ -6,6 +6,6 @@ import { BRC } from '../types/RuntimeCxt';
* @param context
* @param refunds
*/
export declare function updateWithdrawState(context: BRC, id: string): Promise<0 | 1>;
export declare function updateWithdrawState(context: BRC, id: string): Promise<1 | 0>;
declare const triggers: Trigger<EntityDict, 'withdraw', BRC>[];
export default triggers;

View File

@ -1,6 +1,6 @@
import { OakException, OpRecord } from 'oak-domain/lib/types';
import { EntityDict } from '../oak-app-domain/index';
export declare class ExternalPrePayException<ED extends EntityDict> extends OakException<ED> {
export declare class ExternalPayUtilException<ED extends EntityDict> extends OakException<ED> {
reason: any;
constructor(reason: any, message?: string);
getSerialData(): {

View File

@ -1,9 +1,9 @@
import { OakException } from 'oak-domain/lib/types';
import makeDepedentException from './DependentExceptions';
export class ExternalPrePayException extends OakException {
export class ExternalPayUtilException extends OakException {
reason;
constructor(reason, message) {
super(message || '调用外部支付预下单接口失败');
super(message || '调用外部支付渠道接口失败');
this.reason = reason;
}
getSerialData() {
@ -28,7 +28,7 @@ export function makeException(msg) {
const { name, message } = data;
switch (name) {
case 'ExternalPrePayException': {
exception = new ExternalPrePayException(data.reason, message);
exception = new ExternalPayUtilException(data.reason, message);
break;
}
case 'RefundExceedMax': {

View File

@ -1,6 +1,7 @@
import { EntityDict } from '../oak-app-domain';
import { BRC } from '../types/RuntimeCxt';
type IState = EntityDict['pay']['OpSchema']['iState'];
type RefundIState = EntityDict['refund']['OpSchema']['iState'];
export default interface PayClazz {
getAccountEntity(): [string, string];
getAccountAmount(context: BRC): Promise<number>;
@ -16,8 +17,13 @@ export default interface PayClazz {
extra?: EntityDict['pay']['Update']['data'];
price?: number;
}>;
refund(refund: EntityDict['refund']['OpSchema']): Promise<EntityDict['refund']['Update']['data'] | undefined>;
closeRefund(refund: EntityDict['refund']['OpSchema']): Promise<void>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: RefundIState;
extra?: EntityDict['refund']['Update']['data'];
price?: number;
}>;
refund(refund: EntityDict['refund']['Schema'], context: BRC): Promise<EntityDict['refund']['Update']['data'] | undefined>;
getRefundableAt(successAt: number): number;
getRefundState(refund: EntityDict['refund']['OpSchema']): Promise<[EntityDict['refund']['OpSchema']['iState'], EntityDict['refund']['Update']['data'] | undefined]>;
}

View File

@ -9,13 +9,18 @@ export default class Account implements PayClazz {
calcPayTax(): [number, string, string];
getRefundableAt(successAt: number): number;
refund(refund: EntityDict['refund']['Schema']): Promise<undefined>;
closeRefund(refund: EntityDict['refund']['Schema']): Promise<void>;
getRefundState(refund: EntityDict['refund']['Schema']): Promise<[EntityDict['refund']['Schema']['iState'], undefined]>;
decodePayNotification(params: Record<string, any>, body: any): Promise<{
payId: string;
iState: string | null | undefined;
extra?: EntityDict['pay']['Update']['data'] | undefined;
}>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: string | null | undefined;
extra?: EntityDict['refund']['Update']['data'] | undefined;
price?: number | undefined;
}>;
prepay(pay: EntityDict['pay']['Schema'], data: EntityDict['pay']['Update']['data'], context: BRC): Promise<void>;
getState(pay: EntityDict['pay']['Schema']): Promise<[string, EntityDict['pay']['Update']['data']]>;
close(pay: EntityDict['pay']['Schema']): Promise<void>;

View File

@ -23,15 +23,15 @@ export default class Account {
async refund(refund) {
return;
}
async closeRefund(refund) {
return;
}
async getRefundState(refund) {
return ['refuding', undefined];
}
decodePayNotification(params, body) {
throw new Error("account类型的pay不需调用此接口");
}
decodeRefundNotification(params, body) {
throw new Error("account类型的pay不需调用此接口");
}
async prepay(pay, data, context) {
const { entity, entityId, price } = pay;
assert(entity === 'account' && entityId);

View File

@ -13,13 +13,18 @@ export default class Offline implements PayClazz {
calcPayTax(price: number): [number, string, string];
getRefundableAt(successTime: number): number;
refund(refund: RefundOpSchema): Promise<UpdateOperationData | undefined>;
closeRefund(refund: RefundOpSchema): Promise<void>;
getRefundState(refund: RefundOpSchema): Promise<[PayOpSchema['iState'], PayUpdateData | undefined]>;
decodePayNotification(params: Record<string, any>, body: any): Promise<{
payId: string;
iState: string | null | undefined;
extra?: PayUpdateData | undefined;
}>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: string | null | undefined;
extra?: EntityDict['refund']['Update']['data'] | undefined;
price?: number | undefined;
}>;
prepay(pay: PayOpSchema, data: PayUpdateData, context: BRC): Promise<void>;
getState(pay: PayOpSchema): Promise<[string, PayUpdateData]>;
close(pay: PayOpSchema): Promise<void>;

View File

@ -49,10 +49,6 @@ export default class Offline {
// 啥也不做
return;
}
async closeRefund(refund) {
// 啥也不做
return;
}
async getRefundState(refund) {
const { iState } = refund;
assert(iState === 'refunding');
@ -61,6 +57,9 @@ export default class Offline {
decodePayNotification(params, body) {
throw new Error("offline类型的pay不需调用此接口");
}
decodeRefundNotification(params, body) {
throw new Error("offline类型的pay不需调用此接口");
}
async prepay(pay, data, context) {
data.phantom3 = Math.ceil(Math.random() * 1000000);
return;

View File

@ -3,7 +3,7 @@ import PayClazz from "../../../types/PayClazz";
import { BRC } from "../../../types/RuntimeCxt";
import { WechatPay as WechatPaySDK } from 'wechat-pay-nodejs';
import { EntityDict } from "../../../oak-app-domain";
import { OpSchema as OpRefund, UpdateOperationData as RefundUpdateData } from "../../../oak-app-domain/Refund/Schema";
import { OpSchema as OpRefund, UpdateOperationData as RefundUpdateData, Schema as Refund } from "../../../oak-app-domain/Refund/Schema";
import { Schema as WpProduct } from '../../../oak-app-domain/WpProduct/Schema';
import WechatPayDebug from './WechatPay.debug';
export default class WechatPay extends WechatPayDebug implements PayClazz {
@ -19,10 +19,9 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
static MIN_REFUND_DAYS_GAP: number;
static DEFAULT_REFUND_DAYS_GAP: number;
constructor(wpProduct: WpProduct, appId: string);
refund(refund: OpRefund): Promise<RefundUpdateData | undefined>;
closeRefund(refund: OpRefund): Promise<void>;
getRefundState(refund: OpRefund): Promise<[string | null | undefined, PayUpdateData | undefined]>;
private analyzePrepayResult;
refund(refund: Refund, context: BRC): Promise<RefundUpdateData | undefined>;
getRefundState(refund: OpRefund): Promise<[string | null | undefined, RefundUpdateData | undefined]>;
private analyzeResult;
private caclRefundDeadline;
prepay(pay: Pay, data: PayUpdateData, context: BRC): Promise<void>;
getState(pay: OpPay): Promise<[EntityDict['pay']['OpSchema']['iState'], PayUpdateData]>;
@ -32,5 +31,11 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
iState: EntityDict['pay']['OpSchema']['iState'];
extra?: PayUpdateData;
}>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: string | null | undefined;
extra?: RefundUpdateData | undefined;
price?: number | undefined;
}>;
getRefundableAt(successAt: number): number;
}

View File

@ -8,13 +8,18 @@ export declare function registerGetPayStateResult(payState: NonNullable<EntityDi
export default class WechatPay implements PayClazz {
wpProduct: WpProduct;
constructor(wpProduct: WpProduct);
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: string | null | undefined;
extra?: RefundUpdateData | undefined;
price?: number | undefined;
}>;
getAccountEntity(): [string, string];
getAccountAmount(context: BRC): Promise<number>;
calcTransferTax(price: number): [number, string, string];
calcRefundTax(price: number): [number, string, string];
calcPayTax(price: number): [number, string, string];
refund(refund: Refund): Promise<RefundUpdateData | undefined>;
closeRefund(refund: Refund): Promise<void>;
refund(refund: Refund, context: BRC): Promise<RefundUpdateData | undefined>;
getRefundState(refund: Refund): Promise<[string | null | undefined, PayUpdateData | undefined]>;
decodePayNotification(params: Record<string, any>, body: any): Promise<{
payId: string;

View File

@ -8,6 +8,9 @@ export default class WechatPay {
constructor(wpProduct) {
this.wpProduct = wpProduct;
}
decodeRefundNotification(params, body) {
throw new Error('Method not implemented.');
}
getAccountEntity() {
return ['wpAccount', this.wpProduct.wpAccountId];
}
@ -49,14 +52,11 @@ export default class WechatPay {
assert(typeof taxLossRatio === 'number', '微信渠道的手续费率未配置');
return [Math.round(price * taxLossRatio / 100), 'wpAccount', wpProduct.wpAccountId];
}
async refund(refund) {
async refund(refund, context) {
return {
externalId: Math.random().toString(),
};
}
async closeRefund(refund) {
return;
}
async getRefundState(refund) {
const r = Math.random();
if (r < 0.5) {

View File

@ -2,7 +2,7 @@ import fs from 'fs';
import dayJs from 'dayjs';
import { WechatPay as WechatPaySDK } from 'wechat-pay-nodejs';
import { compressTo32, decompressFrom32 } from 'oak-domain/lib/utils/uuid';
import { ExternalPrePayException } from "../../../types/Exception";
import { ExternalPayUtilException } from "../../../types/Exception";
import assert from "assert";
import { omit } from "oak-domain/lib/utils/lodash";
import WechatPayDebug from './WechatPay.debug';
@ -14,6 +14,12 @@ const TRADE_STATE_MATRIX = {
'PAYERROR': 'closed',
'REVOKED': 'closed',
};
const REFUND_STATE_MATRIX = {
'SUCCESS': "successful",
'CLOSED': "failed",
"ABNORMAL": "refunding",
"PROCESSING": 'refunding',
};
export default class WechatPay extends WechatPayDebug {
wechatPay;
refundGapDays;
@ -45,20 +51,45 @@ export default class WechatPay extends WechatPayDebug {
cert_public_content: fs.readFileSync(this.publicKeyFilePath),
});
}
refund(refund) {
throw new Error("Method not implemented.");
async refund(refund, context) {
const { id, price, pay } = refund;
const serverUrl = context.composeAccessPath();
const endpoint = this.refundNotifyUrl;
const refundNotifyUrl = `${serverUrl}/endpoint/${endpoint}/${pay.id}`;
const result = await this.wechatPay.createRefund({
out_trade_no: compressTo32(pay.id),
out_refund_no: compressTo32(id),
notify_url: refundNotifyUrl,
amount: {
refund: price,
total: pay.price,
currency: 'CNY',
}
closeRefund(refund) {
throw new Error("Method not implemented.");
});
const { refund_id } = this.analyzeResult(result);
return {
externalId: refund_id,
};
}
getRefundState(refund) {
throw new Error("Method not implemented.");
async getRefundState(refund) {
const result = await this.wechatPay.queryRefundByOutRefundNo(compressTo32(refund.id));
const { status, success_time } = this.analyzeResult(result);
/**
* 退款状态 退款到银行发现用户的卡作废或者冻结了导致原路退款银行卡失败可前往商户平台pay.weixin.qq.com-交易中心手动处理此笔退款
可选取值
SUCCESS: 退款成功
CLOSED: 退款关闭
PROCESSING: 退款处理中
ABNORMAL: 退款异常
*/
const iState = REFUND_STATE_MATRIX[status];
return [iState, success_time ? { successAt: dayJs(success_time).millisecond() } : undefined];
}
analyzePrepayResult(result) {
analyzeResult(result) {
const { success, data, } = result;
if (!success) {
console.error(JSON.stringify(result));
throw new ExternalPrePayException(result);
throw new ExternalPayUtilException(result);
}
return data;
}
@ -88,7 +119,7 @@ export default class WechatPay extends WechatPayDebug {
total: pay.price,
},
});
const { code_url } = this.analyzePrepayResult(result);
const { code_url } = this.analyzeResult(result);
data.externalId = code_url;
data.meta = {
codeUrl: code_url,
@ -119,7 +150,7 @@ export default class WechatPay extends WechatPayDebug {
openid: wechatUser.openId,
}
});
const prepayMeta = this.analyzePrepayResult(result);
const prepayMeta = this.analyzeResult(result);
const prepayId = prepayMeta.package.slice(11); // `prepay_id=${prepay_id}`
data.externalId = prepayId;
data.meta = {
@ -152,7 +183,7 @@ export default class WechatPay extends WechatPayDebug {
* USERPAYING用户支付中仅付款码支付会返回
* PAYERROR支付失败仅付款码支付会返回
*/
const { trade_state: tradeState, success_time } = this.analyzePrepayResult(result);
const { trade_state: tradeState, success_time } = this.analyzeResult(result);
const iState = TRADE_STATE_MATRIX[tradeState];
assert(iState);
const updateData = {
@ -168,7 +199,7 @@ export default class WechatPay extends WechatPayDebug {
async close(pay) {
const outTradeNo = compressTo32(pay.id);
const result = await this.wechatPay.closeOrder(outTradeNo);
this.analyzePrepayResult(result);
this.analyzeResult(result);
}
async decodePayNotification(params, body) {
const { resource } = body;
@ -285,6 +316,59 @@ export default class WechatPay extends WechatPayDebug {
extra,
};
}
async decodeRefundNotification(params, body) {
const { resource } = body;
if (process.env.NODE_ENV !== 'production') {
console.log('decodeRefundNotification-resource', JSON.stringify(resource));
}
const { ciphertext, nonce, associated_data } = resource;
const result2 = this.wechatPay.decryptAesGcm({
ciphertext,
nonce,
apiV3Key: this.apiV3Key,
associatedData: associated_data,
});
const result = JSON.parse(result2);
if (process.env.NODE_ENV !== 'production') {
console.log('decodeRefundNotification-decrypt', JSON.stringify(result));
}
/**
* 对resource对象进行解密后得到的资源对象示例
* https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/refund-result-notice.html
* {
"mchid": "1900000100",
"transaction_id": "1008450740201411110005820873",
"out_trade_no": "20150806125346",
"refund_id": "50200207182018070300011301001",
"out_refund_no": "7752501201407033233368018",
"refund_status": "SUCCESS",
"success_time": "2018-06-08T10:34:56+08:00",
"user_received_account": "招商银行信用卡0403",
"amount" : {
"total": 999,
"refund": 999,
"payer_total": 999,
"payer_refund": 999
}
}
*/
const { out_refund_no, mchid, refund_status, success_time, } = result;
const refundId = decompressFrom32(out_refund_no);
assert(mchid === this.mchId);
const iState = REFUND_STATE_MATRIX[refund_status];
assert(iState);
const extra = {
meta: omit(result, ['mchid',]),
};
if (iState === 'successful') {
extra.successAt = success_time;
}
return {
refundId,
iState,
extra,
};
}
getRefundableAt(successAt) {
return this.caclRefundDeadline(successAt);
}

View File

@ -1,4 +1,4 @@
import WechatPay from './WechatPay';
import WechatPayDebug from './WechatPay.debug';
declare const _default: typeof WechatPayDebug | typeof WechatPay;
declare const _default: typeof WechatPay | typeof WechatPayDebug;
export default _default;

View File

@ -12,8 +12,8 @@ export declare function getWithdrawCreateData(params: {
creatorId: string;
creator?: import("../oak-app-domain/User/Schema").UpdateOperation | undefined;
} & {
refund$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdraw" | "withdrawId">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdraw" | "withdrawId">>)[] | undefined;
withdrawTransfer$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdraw" | "withdrawId">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdraw" | "withdrawId">>)[] | undefined;
refund$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdrawId" | "withdraw">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdrawId" | "withdraw">>)[] | undefined;
withdrawTransfer$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdrawId" | "withdraw">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdrawId" | "withdraw">>)[] | undefined;
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
@ -26,8 +26,8 @@ export declare function getWithdrawCreateData(params: {
creator?: undefined;
creatorId: string;
} & {
refund$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdraw" | "withdrawId">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdraw" | "withdrawId">>)[] | undefined;
withdrawTransfer$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdraw" | "withdrawId">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdraw" | "withdrawId">>)[] | undefined;
refund$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdrawId" | "withdraw">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdrawId" | "withdraw">>)[] | undefined;
withdrawTransfer$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdrawId" | "withdraw">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdrawId" | "withdraw">>)[] | undefined;
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
@ -40,8 +40,8 @@ export declare function getWithdrawCreateData(params: {
creatorId: string;
creator?: import("../oak-app-domain/User/Schema").UpdateOperation | undefined;
} & {
refund$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdraw" | "withdrawId">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdraw" | "withdrawId">>)[] | undefined;
withdrawTransfer$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdraw" | "withdrawId">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdraw" | "withdrawId">>)[] | undefined;
refund$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdrawId" | "withdraw">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdrawId" | "withdraw">>)[] | undefined;
withdrawTransfer$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdrawId" | "withdraw">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdrawId" | "withdraw">>)[] | undefined;
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
@ -54,8 +54,8 @@ export declare function getWithdrawCreateData(params: {
creator?: undefined;
creatorId: string;
} & {
refund$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdraw" | "withdrawId">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdraw" | "withdrawId">>)[] | undefined;
withdrawTransfer$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdraw" | "withdrawId">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdraw" | "withdrawId">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdraw" | "withdrawId">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdraw" | "withdrawId">>)[] | undefined;
refund$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdrawId" | "withdraw">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/Refund/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/Refund/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/Refund/Schema").CreateOperationData, "withdrawId" | "withdraw">>)[] | undefined;
withdrawTransfer$withdraw?: import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdrawId" | "withdraw">[]> | (import("oak-domain/lib/types").Operation<string, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").UpdateOperationData, "withdrawId" | "withdraw">, Omit<import("../oak-app-domain/WithdrawTransfer/Schema").Filter, "withdrawId" | "withdraw">> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/WithdrawTransfer/Schema").CreateOperationData, "withdrawId" | "withdraw">>)[] | undefined;
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;

View File

@ -15,7 +15,7 @@ export declare class BackendRuntimeContext<ED extends EntityDict & BaseEntityDic
type?: number | undefined;
systemId?: number | undefined;
system?: import("../oak-app-domain/System/Schema").Projection | undefined;
config?: number | import("oak-domain/lib/types").JsonProjection<import("../oak-app-domain/Application/Schema").WechatMpConfig | import("../oak-app-domain/Application/Schema").WebConfig | import("../oak-app-domain/Application/Schema").WechatPublicConfig | import("../oak-app-domain/Application/Schema").NativeConfig> | undefined;
config?: number | import("oak-domain/lib/types").JsonProjection<import("../oak-app-domain/Application/Schema").WebConfig | import("../oak-app-domain/Application/Schema").WechatMpConfig | import("../oak-app-domain/Application/Schema").WechatPublicConfig | import("../oak-app-domain/Application/Schema").NativeConfig> | undefined;
style?: number | import("oak-domain/lib/types").JsonProjection<import("oak-general-business/lib/types/Style").Style> | undefined;
domainId?: number | undefined;
domain?: import("../oak-app-domain/Domain/Schema").Projection | undefined;

View File

@ -1,4 +1,4 @@
import { String, Text, Price } from 'oak-domain/lib/types/DataType';
import { String, Text, Price, Datetime } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc, ActionDef } from 'oak-domain/lib/types';
import { Schema as User } from './User';
@ -14,6 +14,7 @@ export interface Schema extends EntityShape {
price: Price;
creator: User;
reason?: Text;
successAt?: Datetime;
opers: AccountOper[];
}
export type IAction = 'succeed' | 'fail';

View File

@ -23,7 +23,8 @@ exports.entityDesc = {
iState: '状态',
creator: '创建者',
reason: '原因',
opers: '相关账户操作'
opers: '相关账户操作',
successAt: '退款成功时间'
},
action: {
succeed: '退款成功',

View File

@ -3,7 +3,7 @@ import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, MakeFil
import { OneOf } from "oak-domain/lib/types/Polyfill";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, AggregationResult, EntityShape } from "oak-domain/lib/types/Entity";
import { Action, ParticularAction, IState } from "./Action";
import { Price, String, Text } from "oak-domain/lib/types/DataType";
import { Price, String, Text, Datetime } from "oak-domain/lib/types/DataType";
import * as Withdraw from "../Withdraw/Schema";
import * as Pay from "../Pay/Schema";
import * as User from "../User/Schema";
@ -18,6 +18,7 @@ export type OpSchema = EntityShape & {
price: Price;
creatorId: ForeignKey<"user">;
reason?: Text | null;
successAt?: Datetime | null;
iState?: IState | null;
};
export type OpAttr = keyof OpSchema;
@ -30,6 +31,7 @@ export type Schema = EntityShape & {
price: Price;
creatorId: ForeignKey<"user">;
reason?: Text | null;
successAt?: Datetime | null;
iState?: IState | null;
withdraw?: Withdraw.Schema | null;
pay: Pay.Schema;
@ -57,6 +59,7 @@ type AttrFilter = {
creatorId: Q_StringValue;
creator: User.Filter;
reason: Q_StringValue;
successAt: Q_DateValue;
iState: Q_EnumValue<IState>;
sysAccountOper$refund: SysAccountOper.Filter & SubQueryPredicateMetadata;
accountOper$entity: AccountOper.Filter & SubQueryPredicateMetadata;
@ -80,6 +83,7 @@ export type Projection = {
creatorId?: number;
creator?: User.Projection;
reason?: number;
successAt?: number;
iState?: number;
sysAccountOper$refund?: SysAccountOper.Selection & {
$entity: "sysAccountOper";
@ -134,6 +138,8 @@ export type SortAttr = {
creator: User.SortAttr;
} | {
reason: number;
} | {
successAt: number;
} | {
iState: number;
} | {

View File

@ -38,6 +38,9 @@ exports.desc = {
reason: {
type: "text"
},
successAt: {
type: "datetime"
},
iState: {
type: "enum",
enumeration: ["refunding", "successful", "failed"]

View File

@ -1 +1 @@
{ "name": "帐户", "attr": { "pay": "关联支付", "price": "价格", "loss": "损耗", "withdraw": "关联提现", "meta": "metadata", "externalId": "外部退款流水号", "iState": "状态", "creator": "创建者", "reason": "原因", "opers": "相关账户操作" }, "action": { "succeed": "退款成功", "fail": "退款失败" }, "v": { "iState": { "refunding": "退款中", "successful": "退款成功", "failed": "退款失败" } } }
{ "name": "帐户", "attr": { "pay": "关联支付", "price": "价格", "loss": "损耗", "withdraw": "关联提现", "meta": "metadata", "externalId": "外部退款流水号", "iState": "状态", "creator": "创建者", "reason": "原因", "opers": "相关账户操作", "successAt": "退款成功时间" }, "action": { "succeed": "退款成功", "fail": "退款失败" }, "v": { "iState": { "refunding": "退款中", "successful": "退款成功", "failed": "退款失败" } } }

View File

@ -325,7 +325,7 @@ const triggers = [
return;
}
}
const data = await payClazz.refund(refund);
const data = await payClazz.refund(refund, context);
if (data) {
(0, assert_1.default)(data.externalId);
const closeFn = context.openRootMode();

View File

@ -1,6 +1,6 @@
import { OakException, OpRecord } from 'oak-domain/lib/types';
import { EntityDict } from '../oak-app-domain/index';
export declare class ExternalPrePayException<ED extends EntityDict> extends OakException<ED> {
export declare class ExternalPayUtilException<ED extends EntityDict> extends OakException<ED> {
reason: any;
constructor(reason: any, message?: string);
getSerialData(): {

View File

@ -1,13 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeException = exports.RefundExceedMax = exports.ExternalPrePayException = void 0;
exports.makeException = exports.RefundExceedMax = exports.ExternalPayUtilException = void 0;
const tslib_1 = require("tslib");
const types_1 = require("oak-domain/lib/types");
const DependentExceptions_1 = tslib_1.__importDefault(require("./DependentExceptions"));
class ExternalPrePayException extends types_1.OakException {
class ExternalPayUtilException extends types_1.OakException {
reason;
constructor(reason, message) {
super(message || '调用外部支付预下单接口失败');
super(message || '调用外部支付渠道接口失败');
this.reason = reason;
}
getSerialData() {
@ -18,7 +18,7 @@ class ExternalPrePayException extends types_1.OakException {
};
}
}
exports.ExternalPrePayException = ExternalPrePayException;
exports.ExternalPayUtilException = ExternalPayUtilException;
class RefundExceedMax extends types_1.OakException {
constructor(message) {
super(message || '可退款的总额不足');
@ -34,7 +34,7 @@ function makeException(msg) {
const { name, message } = data;
switch (name) {
case 'ExternalPrePayException': {
exception = new ExternalPrePayException(data.reason, message);
exception = new ExternalPayUtilException(data.reason, message);
break;
}
case 'RefundExceedMax': {

View File

@ -1,6 +1,7 @@
import { EntityDict } from '../oak-app-domain';
import { BRC } from '../types/RuntimeCxt';
type IState = EntityDict['pay']['OpSchema']['iState'];
type RefundIState = EntityDict['refund']['OpSchema']['iState'];
export default interface PayClazz {
getAccountEntity(): [string, string];
getAccountAmount(context: BRC): Promise<number>;
@ -16,8 +17,13 @@ export default interface PayClazz {
extra?: EntityDict['pay']['Update']['data'];
price?: number;
}>;
refund(refund: EntityDict['refund']['OpSchema']): Promise<EntityDict['refund']['Update']['data'] | undefined>;
closeRefund(refund: EntityDict['refund']['OpSchema']): Promise<void>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: RefundIState;
extra?: EntityDict['refund']['Update']['data'];
price?: number;
}>;
refund(refund: EntityDict['refund']['Schema'], context: BRC): Promise<EntityDict['refund']['Update']['data'] | undefined>;
getRefundableAt(successAt: number): number;
getRefundState(refund: EntityDict['refund']['OpSchema']): Promise<[EntityDict['refund']['OpSchema']['iState'], EntityDict['refund']['Update']['data'] | undefined]>;
}

View File

@ -14,7 +14,7 @@ export declare const mergedProjection: {
type?: number | undefined;
systemId?: number | undefined;
system?: import("../oak-app-domain/System/Schema").Projection | undefined;
config?: number | import("oak-domain/lib/types").JsonProjection<import("../oak-app-domain/Application/Schema").WechatMpConfig | import("../oak-app-domain/Application/Schema").WebConfig | import("../oak-app-domain/Application/Schema").WechatPublicConfig | import("../oak-app-domain/Application/Schema").NativeConfig> | undefined;
config?: number | import("oak-domain/lib/types").JsonProjection<import("../oak-app-domain/Application/Schema").WebConfig | import("../oak-app-domain/Application/Schema").WechatMpConfig | import("../oak-app-domain/Application/Schema").WechatPublicConfig | import("../oak-app-domain/Application/Schema").NativeConfig> | undefined;
style?: number | import("oak-domain/lib/types").JsonProjection<import("oak-general-business/lib/types/Style").Style> | undefined;
domainId?: number | undefined;
domain?: import("../oak-app-domain/Domain/Schema").Projection | undefined;

View File

@ -9,13 +9,18 @@ export default class Account implements PayClazz {
calcPayTax(): [number, string, string];
getRefundableAt(successAt: number): number;
refund(refund: EntityDict['refund']['Schema']): Promise<undefined>;
closeRefund(refund: EntityDict['refund']['Schema']): Promise<void>;
getRefundState(refund: EntityDict['refund']['Schema']): Promise<[EntityDict['refund']['Schema']['iState'], undefined]>;
decodePayNotification(params: Record<string, any>, body: any): Promise<{
payId: string;
iState: string | null | undefined;
extra?: EntityDict['pay']['Update']['data'] | undefined;
}>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: string | null | undefined;
extra?: EntityDict['refund']['Update']['data'] | undefined;
price?: number | undefined;
}>;
prepay(pay: EntityDict['pay']['Schema'], data: EntityDict['pay']['Update']['data'], context: BRC): Promise<void>;
getState(pay: EntityDict['pay']['Schema']): Promise<[string, EntityDict['pay']['Update']['data']]>;
close(pay: EntityDict['pay']['Schema']): Promise<void>;

View File

@ -26,15 +26,15 @@ class Account {
async refund(refund) {
return;
}
async closeRefund(refund) {
return;
}
async getRefundState(refund) {
return ['refuding', undefined];
}
decodePayNotification(params, body) {
throw new Error("account类型的pay不需调用此接口");
}
decodeRefundNotification(params, body) {
throw new Error("account类型的pay不需调用此接口");
}
async prepay(pay, data, context) {
const { entity, entityId, price } = pay;
(0, assert_1.default)(entity === 'account' && entityId);

View File

@ -13,13 +13,18 @@ export default class Offline implements PayClazz {
calcPayTax(price: number): [number, string, string];
getRefundableAt(successTime: number): number;
refund(refund: RefundOpSchema): Promise<UpdateOperationData | undefined>;
closeRefund(refund: RefundOpSchema): Promise<void>;
getRefundState(refund: RefundOpSchema): Promise<[PayOpSchema['iState'], PayUpdateData | undefined]>;
decodePayNotification(params: Record<string, any>, body: any): Promise<{
payId: string;
iState: string | null | undefined;
extra?: PayUpdateData | undefined;
}>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: string | null | undefined;
extra?: EntityDict['refund']['Update']['data'] | undefined;
price?: number | undefined;
}>;
prepay(pay: PayOpSchema, data: PayUpdateData, context: BRC): Promise<void>;
getState(pay: PayOpSchema): Promise<[string, PayUpdateData]>;
close(pay: PayOpSchema): Promise<void>;

View File

@ -52,10 +52,6 @@ class Offline {
// 啥也不做
return;
}
async closeRefund(refund) {
// 啥也不做
return;
}
async getRefundState(refund) {
const { iState } = refund;
(0, assert_1.default)(iState === 'refunding');
@ -64,6 +60,9 @@ class Offline {
decodePayNotification(params, body) {
throw new Error("offline类型的pay不需调用此接口");
}
decodeRefundNotification(params, body) {
throw new Error("offline类型的pay不需调用此接口");
}
async prepay(pay, data, context) {
data.phantom3 = Math.ceil(Math.random() * 1000000);
return;

View File

@ -3,7 +3,7 @@ import PayClazz from "../../../types/PayClazz";
import { BRC } from "../../../types/RuntimeCxt";
import { WechatPay as WechatPaySDK } from 'wechat-pay-nodejs';
import { EntityDict } from "../../../oak-app-domain";
import { OpSchema as OpRefund, UpdateOperationData as RefundUpdateData } from "../../../oak-app-domain/Refund/Schema";
import { OpSchema as OpRefund, UpdateOperationData as RefundUpdateData, Schema as Refund } from "../../../oak-app-domain/Refund/Schema";
import { Schema as WpProduct } from '../../../oak-app-domain/WpProduct/Schema';
import WechatPayDebug from './WechatPay.debug';
export default class WechatPay extends WechatPayDebug implements PayClazz {
@ -19,10 +19,9 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
static MIN_REFUND_DAYS_GAP: number;
static DEFAULT_REFUND_DAYS_GAP: number;
constructor(wpProduct: WpProduct, appId: string);
refund(refund: OpRefund): Promise<RefundUpdateData | undefined>;
closeRefund(refund: OpRefund): Promise<void>;
getRefundState(refund: OpRefund): Promise<[string | null | undefined, PayUpdateData | undefined]>;
private analyzePrepayResult;
refund(refund: Refund, context: BRC): Promise<RefundUpdateData | undefined>;
getRefundState(refund: OpRefund): Promise<[string | null | undefined, RefundUpdateData | undefined]>;
private analyzeResult;
private caclRefundDeadline;
prepay(pay: Pay, data: PayUpdateData, context: BRC): Promise<void>;
getState(pay: OpPay): Promise<[EntityDict['pay']['OpSchema']['iState'], PayUpdateData]>;
@ -32,5 +31,11 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
iState: EntityDict['pay']['OpSchema']['iState'];
extra?: PayUpdateData;
}>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: string | null | undefined;
extra?: RefundUpdateData | undefined;
price?: number | undefined;
}>;
getRefundableAt(successAt: number): number;
}

View File

@ -8,13 +8,18 @@ export declare function registerGetPayStateResult(payState: NonNullable<EntityDi
export default class WechatPay implements PayClazz {
wpProduct: WpProduct;
constructor(wpProduct: WpProduct);
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string;
iState: string | null | undefined;
extra?: RefundUpdateData | undefined;
price?: number | undefined;
}>;
getAccountEntity(): [string, string];
getAccountAmount(context: BRC): Promise<number>;
calcTransferTax(price: number): [number, string, string];
calcRefundTax(price: number): [number, string, string];
calcPayTax(price: number): [number, string, string];
refund(refund: Refund): Promise<RefundUpdateData | undefined>;
closeRefund(refund: Refund): Promise<void>;
refund(refund: Refund, context: BRC): Promise<RefundUpdateData | undefined>;
getRefundState(refund: Refund): Promise<[string | null | undefined, PayUpdateData | undefined]>;
decodePayNotification(params: Record<string, any>, body: any): Promise<{
payId: string;

View File

@ -13,6 +13,9 @@ class WechatPay {
constructor(wpProduct) {
this.wpProduct = wpProduct;
}
decodeRefundNotification(params, body) {
throw new Error('Method not implemented.');
}
getAccountEntity() {
return ['wpAccount', this.wpProduct.wpAccountId];
}
@ -54,14 +57,11 @@ class WechatPay {
(0, assert_1.default)(typeof taxLossRatio === 'number', '微信渠道的手续费率未配置');
return [Math.round(price * taxLossRatio / 100), 'wpAccount', wpProduct.wpAccountId];
}
async refund(refund) {
async refund(refund, context) {
return {
externalId: Math.random().toString(),
};
}
async closeRefund(refund) {
return;
}
async getRefundState(refund) {
const r = Math.random();
if (r < 0.5) {

View File

@ -17,6 +17,12 @@ const TRADE_STATE_MATRIX = {
'PAYERROR': 'closed',
'REVOKED': 'closed',
};
const REFUND_STATE_MATRIX = {
'SUCCESS': "successful",
'CLOSED': "failed",
"ABNORMAL": "refunding",
"PROCESSING": 'refunding',
};
class WechatPay extends WechatPay_debug_1.default {
wechatPay;
refundGapDays;
@ -48,20 +54,45 @@ class WechatPay extends WechatPay_debug_1.default {
cert_public_content: fs_1.default.readFileSync(this.publicKeyFilePath),
});
}
refund(refund) {
throw new Error("Method not implemented.");
async refund(refund, context) {
const { id, price, pay } = refund;
const serverUrl = context.composeAccessPath();
const endpoint = this.refundNotifyUrl;
const refundNotifyUrl = `${serverUrl}/endpoint/${endpoint}/${pay.id}`;
const result = await this.wechatPay.createRefund({
out_trade_no: (0, uuid_1.compressTo32)(pay.id),
out_refund_no: (0, uuid_1.compressTo32)(id),
notify_url: refundNotifyUrl,
amount: {
refund: price,
total: pay.price,
currency: 'CNY',
}
closeRefund(refund) {
throw new Error("Method not implemented.");
});
const { refund_id } = this.analyzeResult(result);
return {
externalId: refund_id,
};
}
getRefundState(refund) {
throw new Error("Method not implemented.");
async getRefundState(refund) {
const result = await this.wechatPay.queryRefundByOutRefundNo((0, uuid_1.compressTo32)(refund.id));
const { status, success_time } = this.analyzeResult(result);
/**
* 退款状态 退款到银行发现用户的卡作废或者冻结了导致原路退款银行卡失败可前往商户平台pay.weixin.qq.com-交易中心手动处理此笔退款
可选取值
SUCCESS: 退款成功
CLOSED: 退款关闭
PROCESSING: 退款处理中
ABNORMAL: 退款异常
*/
const iState = REFUND_STATE_MATRIX[status];
return [iState, success_time ? { successAt: (0, dayjs_1.default)(success_time).millisecond() } : undefined];
}
analyzePrepayResult(result) {
analyzeResult(result) {
const { success, data, } = result;
if (!success) {
console.error(JSON.stringify(result));
throw new Exception_1.ExternalPrePayException(result);
throw new Exception_1.ExternalPayUtilException(result);
}
return data;
}
@ -91,7 +122,7 @@ class WechatPay extends WechatPay_debug_1.default {
total: pay.price,
},
});
const { code_url } = this.analyzePrepayResult(result);
const { code_url } = this.analyzeResult(result);
data.externalId = code_url;
data.meta = {
codeUrl: code_url,
@ -122,7 +153,7 @@ class WechatPay extends WechatPay_debug_1.default {
openid: wechatUser.openId,
}
});
const prepayMeta = this.analyzePrepayResult(result);
const prepayMeta = this.analyzeResult(result);
const prepayId = prepayMeta.package.slice(11); // `prepay_id=${prepay_id}`
data.externalId = prepayId;
data.meta = {
@ -155,7 +186,7 @@ class WechatPay extends WechatPay_debug_1.default {
* USERPAYING用户支付中仅付款码支付会返回
* PAYERROR支付失败仅付款码支付会返回
*/
const { trade_state: tradeState, success_time } = this.analyzePrepayResult(result);
const { trade_state: tradeState, success_time } = this.analyzeResult(result);
const iState = TRADE_STATE_MATRIX[tradeState];
(0, assert_1.default)(iState);
const updateData = {
@ -171,7 +202,7 @@ class WechatPay extends WechatPay_debug_1.default {
async close(pay) {
const outTradeNo = (0, uuid_1.compressTo32)(pay.id);
const result = await this.wechatPay.closeOrder(outTradeNo);
this.analyzePrepayResult(result);
this.analyzeResult(result);
}
async decodePayNotification(params, body) {
const { resource } = body;
@ -288,6 +319,59 @@ class WechatPay extends WechatPay_debug_1.default {
extra,
};
}
async decodeRefundNotification(params, body) {
const { resource } = body;
if (process.env.NODE_ENV !== 'production') {
console.log('decodeRefundNotification-resource', JSON.stringify(resource));
}
const { ciphertext, nonce, associated_data } = resource;
const result2 = this.wechatPay.decryptAesGcm({
ciphertext,
nonce,
apiV3Key: this.apiV3Key,
associatedData: associated_data,
});
const result = JSON.parse(result2);
if (process.env.NODE_ENV !== 'production') {
console.log('decodeRefundNotification-decrypt', JSON.stringify(result));
}
/**
* 对resource对象进行解密后得到的资源对象示例
* https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/refund-result-notice.html
* {
"mchid": "1900000100",
"transaction_id": "1008450740201411110005820873",
"out_trade_no": "20150806125346",
"refund_id": "50200207182018070300011301001",
"out_refund_no": "7752501201407033233368018",
"refund_status": "SUCCESS",
"success_time": "2018-06-08T10:34:56+08:00",
"user_received_account": "招商银行信用卡0403",
"amount" : {
"total": 999,
"refund": 999,
"payer_total": 999,
"payer_refund": 999
}
}
*/
const { out_refund_no, mchid, refund_status, success_time, } = result;
const refundId = (0, uuid_1.decompressFrom32)(out_refund_no);
(0, assert_1.default)(mchid === this.mchId);
const iState = REFUND_STATE_MATRIX[refund_status];
(0, assert_1.default)(iState);
const extra = {
meta: (0, lodash_1.omit)(result, ['mchid',]),
};
if (iState === 'successful') {
extra.successAt = success_time;
}
return {
refundId,
iState,
extra,
};
}
getRefundableAt(successAt) {
return this.caclRefundDeadline(successAt);
}

View File

@ -2,8 +2,9 @@
* EntityDict的重新声明
* by Xc 20230807
*/
// @ts-nocheck
// @ts-nocheck
import React from 'react';
import { EntityDict } from '../oak-app-domain';
import { TableProps, PaginationProps } from 'antd';

View File

@ -22,6 +22,7 @@ export interface Schema extends EntityShape {
price: Price;
creator: User;
reason?: Text;
successAt?: Datetime;
opers: AccountOper[];
};
@ -53,7 +54,8 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
iState: '状态',
creator: '创建者',
reason: '原因',
opers: '相关账户操作'
opers: '相关账户操作',
successAt: '退款成功时间'
},
action: {
succeed: '退款成功',

View File

@ -362,7 +362,7 @@ const triggers: Trigger<EntityDict, 'refund', BRC>[] = [
}
const data = await payClazz.refund(refund as EntityDict['refund']['Schema']);
const data = await payClazz.refund(refund as EntityDict['refund']['Schema'], context);
if (data) {
assert(data.externalId);
const closeFn = context.openRootMode();

View File

@ -2,10 +2,10 @@ import { OakException, OakUserException, OpRecord } from 'oak-domain/lib/types';
import { EntityDict } from '@oak-app-domain';
import makeDepedentException from './DependentExceptions';
export class ExternalPrePayException<ED extends EntityDict> extends OakException<ED> {
export class ExternalPayUtilException<ED extends EntityDict> extends OakException<ED> {
reason: any;
constructor(reason: any, message?: string) {
super(message || '调用外部支付预下单接口失败');
super(message || '调用外部支付渠道接口失败');
this.reason = reason;
}
@ -35,7 +35,7 @@ export function makeException<ED extends EntityDict>(msg: string | object) {
const { name, message } = data;
switch (name) {
case 'ExternalPrePayException': {
exception = new ExternalPrePayException<ED>(data.reason, message);
exception = new ExternalPayUtilException<ED>(data.reason, message);
break;
}
case 'RefundExceedMax': {

View File

@ -1,6 +1,7 @@
import { EntityDict } from '../oak-app-domain';
import { BRC } from '../types/RuntimeCxt';
type IState = EntityDict['pay']['OpSchema']['iState'];
type RefundIState = EntityDict['refund']['OpSchema']['iState'];
export default interface PayClazz {
getAccountEntity(): [string, string];
@ -26,10 +27,15 @@ export default interface PayClazz {
price?: number;
}>;
// refund相关的待定
refund(refund: EntityDict['refund']['OpSchema']): Promise<EntityDict['refund']['Update']['data'] | undefined>;
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{
refundId: string,
iState: RefundIState,
extra?: EntityDict['refund']['Update']['data'],
price?: number;
}>
closeRefund(refund: EntityDict['refund']['OpSchema']): Promise<void>;
// refund相关的待定
refund(refund: EntityDict['refund']['Schema'], context: BRC): Promise<EntityDict['refund']['Update']['data'] | undefined>;
getRefundableAt(successAt: number): number;

View File

@ -27,9 +27,6 @@ export default class Account implements PayClazz {
async refund(refund: EntityDict['refund']['Schema']): Promise<undefined> {
return;
}
async closeRefund(refund: EntityDict['refund']['Schema']): Promise<void> {
return;
}
async getRefundState(refund: EntityDict['refund']['Schema']): Promise<[EntityDict['refund']['Schema']['iState'], undefined]> {
return ['refuding', undefined];
}
@ -41,6 +38,10 @@ export default class Account implements PayClazz {
throw new Error("account类型的pay不需调用此接口");
}
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{ refundId: string; iState: string | null | undefined; extra?: EntityDict['refund']['Update']['data'] | undefined; price?: number | undefined; }> {
throw new Error("account类型的pay不需调用此接口");
}
async prepay(pay: EntityDict['pay']['Schema'], data: EntityDict['pay']['Update']['data'], context: BRC) {
const { entity, entityId, price } = pay;
assert(entity === 'account' && entityId);

View File

@ -60,10 +60,6 @@ export default class Offline implements PayClazz {
// 啥也不做
return;
}
async closeRefund(refund: RefundOpSchema): Promise<void> {
// 啥也不做
return;
}
async getRefundState(refund: RefundOpSchema): Promise<[PayOpSchema['iState'], PayUpdateData | undefined]> {
const { iState } = refund;
assert(iState === 'refunding');
@ -72,6 +68,9 @@ export default class Offline implements PayClazz {
decodePayNotification(params: Record<string, any>, body: any): Promise<{ payId: string; iState: string | null | undefined; extra?: PayUpdateData | undefined; }> {
throw new Error("offline类型的pay不需调用此接口");
}
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{ refundId: string; iState: string | null | undefined; extra?: EntityDict['refund']['Update']['data'] | undefined; price?: number | undefined; }> {
throw new Error("offline类型的pay不需调用此接口");
}
async prepay(pay: PayOpSchema, data: PayUpdateData, context: BRC): Promise<void> {
data.phantom3 = Math.ceil(Math.random() * 1000000);
return;

View File

@ -17,6 +17,9 @@ export default class WechatPay implements PayClazz {
constructor(wpProduct: WpProduct) {
this.wpProduct = wpProduct;
}
decodeRefundNotification(params: Record<string, any>, body: any): Promise<{ refundId: string; iState: string | null | undefined; extra?: RefundUpdateData | undefined; price?: number | undefined; }> {
throw new Error('Method not implemented.');
}
getAccountEntity(): [string, string] {
return ['wpAccount', this.wpProduct.wpAccountId];
}
@ -61,14 +64,11 @@ export default class WechatPay implements PayClazz {
assert(typeof taxLossRatio === 'number', '微信渠道的手续费率未配置');
return [Math.round(price * taxLossRatio / 100), 'wpAccount', wpProduct.wpAccountId!];
}
async refund(refund: Refund): Promise<RefundUpdateData | undefined> {
async refund(refund: Refund, context: BRC): Promise<RefundUpdateData | undefined> {
return {
externalId: Math.random().toString(),
};
}
async closeRefund(refund: Refund): Promise<void> {
return;
}
async getRefundState(refund: Refund): Promise<[string | null | undefined, PayUpdateData | undefined]> {
const r = Math.random();
if (r < 0.5) {

View File

@ -1,12 +1,12 @@
import { OpSchema as OpPay, Schema as Pay, UpdateOperationData as PayUpdateData } from "../../../oak-app-domain/Pay/Schema";
import PayClazz from "../../../types/PayClazz";
import fs from 'fs';
import fs, { stat } from 'fs';
import dayJs from 'dayjs';
import { BRC } from "../../../types/RuntimeCxt";
import { WechatPay as WechatPaySDK } from 'wechat-pay-nodejs';
import { compressTo32, decompressFrom32 } from 'oak-domain/lib/utils/uuid';
import { ExternalPrePayException } from "../../../types/Exception";
import { ExternalPayUtilException } from "../../../types/Exception";
import { ApiResult, QueryOrderResult } from "wechat-pay-nodejs/typings";
import assert from "assert";
import { EntityDict } from "../../../oak-app-domain";
@ -24,6 +24,13 @@ const TRADE_STATE_MATRIX: Record<string, EntityDict['pay']['OpSchema']['iState']
'REVOKED': 'closed',
};
const REFUND_STATE_MATRIX: Record<string, EntityDict['refund']['OpSchema']['iState']> = {
'SUCCESS': "successful",
'CLOSED': "failed",
"ABNORMAL": "refunding",
"PROCESSING": 'refunding',
};
export default class WechatPay extends WechatPayDebug implements PayClazz {
wechatPay: WechatPaySDK;
@ -61,24 +68,51 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
});
}
refund(refund: OpRefund): Promise<RefundUpdateData | undefined> {
throw new Error("Method not implemented.");
async refund(refund: Refund, context: BRC): Promise<RefundUpdateData | undefined> {
const { id, price, pay } = refund;
const serverUrl = context.composeAccessPath();
const endpoint = this.refundNotifyUrl;
const refundNotifyUrl = `${serverUrl}/endpoint/${endpoint}/${pay.id}`;
const result = await this.wechatPay.createRefund({
out_trade_no: compressTo32(pay.id),
out_refund_no: compressTo32(id),
notify_url: refundNotifyUrl,
amount: {
refund: price!,
total: pay.price!,
currency: 'CNY',
}
closeRefund(refund: OpRefund): Promise<void> {
throw new Error("Method not implemented.");
});
const { refund_id } = this.analyzeResult(result);
return {
externalId: refund_id,
};
}
getRefundState(refund: OpRefund): Promise<[string | null | undefined, PayUpdateData | undefined]> {
throw new Error("Method not implemented.");
async getRefundState(refund: OpRefund): Promise<[string | null | undefined, RefundUpdateData | undefined]> {
const result = await this.wechatPay.queryRefundByOutRefundNo(compressTo32(refund.id!));
const { status, success_time } = this.analyzeResult(result);
/**
* 退 退退pay.weixin.qq.com-退
SUCCESS: 退款成功
CLOSED: 退款关闭
PROCESSING: 退款处理中
ABNORMAL: 退款异常
*/
const iState = REFUND_STATE_MATRIX[status];
return [iState, success_time ? { successAt: dayJs(success_time).millisecond() } : undefined];
}
private analyzePrepayResult<R extends any>(result: ApiResult<R>) {
private analyzeResult<R extends any>(result: ApiResult<R>) {
const {
success,
data,
} = result;
if (!success) {
console.error(JSON.stringify(result));
throw new ExternalPrePayException(result);
throw new ExternalPayUtilException(result);
}
return data!;
}
@ -110,7 +144,7 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
total: pay.price,
},
});
const { code_url } = this.analyzePrepayResult(result);
const { code_url } = this.analyzeResult(result);
data.externalId = code_url;
data.meta = {
codeUrl: code_url,
@ -141,7 +175,7 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
openid: wechatUser.openId!,
}
});
const prepayMeta = this.analyzePrepayResult(result);
const prepayMeta = this.analyzeResult(result);
const prepayId = prepayMeta.package.slice(11); // `prepay_id=${prepay_id}`
data.externalId = prepayId;
data.meta = {
@ -176,7 +210,7 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
* USERPAYING
* PAYERROR
*/
const { trade_state: tradeState, success_time } = this.analyzePrepayResult(result) as QueryOrderResult & {
const { trade_state: tradeState, success_time } = this.analyzeResult(result) as QueryOrderResult & {
trade_state: string;
};
const iState: EntityDict['pay']['OpSchema']['iState'] = TRADE_STATE_MATRIX[tradeState];
@ -195,7 +229,7 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
async close(pay: OpPay): Promise<void> {
const outTradeNo = compressTo32(pay.id);
const result = await this.wechatPay.closeOrder(outTradeNo);
this.analyzePrepayResult(result);
this.analyzeResult(result);
}
async decodePayNotification(params: Record<string, any>, body: any): Promise<{
@ -339,6 +373,82 @@ export default class WechatPay extends WechatPayDebug implements PayClazz {
};
}
async decodeRefundNotification(params: Record<string, any>, body: any): Promise<{ refundId: string; iState: string | null | undefined; extra?: RefundUpdateData | undefined; price?: number | undefined; }> {
const {
resource
} = body as {
resource: {
algorithm: string;
ciphertext: string;
associated_data: string;
original_type: string;
nonce: string;
};
};
if (process.env.NODE_ENV !== 'production') {
console.log('decodeRefundNotification-resource', JSON.stringify(resource));
}
const { ciphertext, nonce, associated_data } = resource;
const result2 = this.wechatPay.decryptAesGcm({
ciphertext,
nonce,
apiV3Key: this.apiV3Key,
associatedData: associated_data,
});
const result = JSON.parse(result2);
if (process.env.NODE_ENV !== 'production') {
console.log('decodeRefundNotification-decrypt', JSON.stringify(result));
}
/**
* resource对象进行解密后
* https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/refund-result-notice.html
* {
"mchid": "1900000100",
"transaction_id": "1008450740201411110005820873",
"out_trade_no": "20150806125346",
"refund_id": "50200207182018070300011301001",
"out_refund_no": "7752501201407033233368018",
"refund_status": "SUCCESS",
"success_time": "2018-06-08T10:34:56+08:00",
"user_received_account": "招商银行信用卡0403",
"amount" : {
"total": 999,
"refund": 999,
"payer_total": 999,
"payer_refund": 999
}
}
*/
const {
out_refund_no,
mchid,
refund_status,
success_time,
} = result;
const refundId = decompressFrom32(out_refund_no);
assert(mchid === this.mchId);
const iState = REFUND_STATE_MATRIX[refund_status];
assert(iState);
const extra: RefundUpdateData = {
meta: omit(result, ['mchid', ]),
};
if (iState === 'successful') {
extra.successAt = success_time;
}
return {
refundId,
iState,
extra,
};
}
getRefundableAt(successAt: number): number {
return this.caclRefundDeadline(successAt);
}