Merge branch 'dev' of gitea.51mars.com:Oak-Team/oak-pay-business into dev
This commit is contained in:
commit
849c4179e4
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ export const entityDesc = {
|
|||
iState: '状态',
|
||||
creator: '创建者',
|
||||
reason: '原因',
|
||||
opers: '相关账户操作'
|
||||
opers: '相关账户操作',
|
||||
successAt: '退款成功时间'
|
||||
},
|
||||
action: {
|
||||
succeed: '退款成功',
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
} | {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ export const desc = {
|
|||
reason: {
|
||||
type: "text"
|
||||
},
|
||||
successAt: {
|
||||
type: "datetime"
|
||||
},
|
||||
iState: {
|
||||
type: "enum",
|
||||
enumeration: ["refunding", "successful", "failed"]
|
||||
|
|
|
|||
|
|
@ -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": "退款失败" } } }
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(): {
|
||||
|
|
|
|||
|
|
@ -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': {
|
||||
|
|
|
|||
|
|
@ -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]>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ exports.entityDesc = {
|
|||
iState: '状态',
|
||||
creator: '创建者',
|
||||
reason: '原因',
|
||||
opers: '相关账户操作'
|
||||
opers: '相关账户操作',
|
||||
successAt: '退款成功时间'
|
||||
},
|
||||
action: {
|
||||
succeed: '退款成功',
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
} | {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ exports.desc = {
|
|||
reason: {
|
||||
type: "text"
|
||||
},
|
||||
successAt: {
|
||||
type: "datetime"
|
||||
},
|
||||
iState: {
|
||||
type: "enum",
|
||||
enumeration: ["refunding", "successful", "failed"]
|
||||
|
|
|
|||
|
|
@ -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": "退款失败" } } }
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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(): {
|
||||
|
|
|
|||
|
|
@ -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': {
|
||||
|
|
|
|||
|
|
@ -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]>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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: '退款成功',
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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': {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue