wechatPay.fontend的部分调试
This commit is contained in:
parent
2fb6fa4f4b
commit
821c73beb5
|
|
@ -17,16 +17,16 @@ declare const List: <T extends keyof EntityDict>(props: ReactComponentProps<Enti
|
|||
data: RowWithActions<EntityDict, T>[];
|
||||
loading: boolean;
|
||||
tablePagination?: any;
|
||||
rowSelection?: {
|
||||
type: "checkbox" | "radio";
|
||||
selectedRowKeys?: string[] | undefined;
|
||||
onChange: (selectedRowKeys: string[], row: RowWithActions<EntityDict, T>[], info?: {
|
||||
type: "multiple" | "single" | "none";
|
||||
} | undefined) => void;
|
||||
} | undefined;
|
||||
hideHeader: boolean;
|
||||
size?: "small" | "large" | "middle" | undefined;
|
||||
scroll?: any;
|
||||
rowSelection?: any;
|
||||
hideHeader?: boolean | undefined;
|
||||
disableSerialNumber?: boolean | undefined;
|
||||
size?: "small" | "middle" | "large" | undefined;
|
||||
scroll?: ({
|
||||
x?: string | number | true | undefined;
|
||||
y?: string | number | undefined;
|
||||
} & {
|
||||
scrollToFirstRowOnChange?: boolean | undefined;
|
||||
}) | undefined;
|
||||
locale?: any;
|
||||
}>) => React.ReactElement;
|
||||
declare const ListPro: <T extends keyof EntityDict>(props: {
|
||||
|
|
@ -43,15 +43,9 @@ declare const ListPro: <T extends keyof EntityDict>(props: {
|
|||
data: RowWithActions<EntityDict, T>[];
|
||||
loading?: boolean | undefined;
|
||||
tablePagination?: any;
|
||||
rowSelection?: {
|
||||
type: "checkbox" | "radio";
|
||||
selectedRowKeys?: string[] | undefined;
|
||||
onChange: (selectedRowKeys: string[], row: RowWithActions<EntityDict, T>[], info?: {
|
||||
type: "multiple" | "single" | "none";
|
||||
} | undefined) => void;
|
||||
} | undefined;
|
||||
rowSelection?: any;
|
||||
disableSerialNumber?: boolean | undefined;
|
||||
size?: "small" | "large" | "middle" | undefined;
|
||||
size?: "small" | "middle" | "large" | undefined;
|
||||
scroll?: any;
|
||||
locale?: any;
|
||||
}) => React.ReactElement;
|
||||
|
|
@ -62,14 +56,14 @@ declare const Detail: <T extends keyof EntityDict>(props: ReactComponentProps<En
|
|||
data: Partial<EntityDict[T]["Schema"]>;
|
||||
title?: string | undefined;
|
||||
bordered?: boolean | undefined;
|
||||
layout?: "horizontal" | "vertical" | undefined;
|
||||
layout?: "vertical" | "horizontal" | undefined;
|
||||
}>) => React.ReactElement;
|
||||
declare const Upsert: <T extends keyof EntityDict>(props: ReactComponentProps<EntityDict, T, false, {
|
||||
helps: Record<string, string>;
|
||||
entity: T;
|
||||
attributes: OakAbsAttrUpsertDef<EntityDict, T, string | number>[];
|
||||
data: EntityDict[T]["Schema"];
|
||||
layout: "horizontal" | "vertical";
|
||||
layout: "vertical" | "horizontal";
|
||||
mode: "default" | "card";
|
||||
}>) => React.ReactElement;
|
||||
export { FilterPanel, List, ListPro, Detail, Upsert, ReactComponentProps, ColumnProps, RowWithActions, OakExtraActionProps, OakAbsAttrDef, onActionFnDef, };
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { CentToString } from 'oak-domain/lib/utils/money';
|
|||
import classNames from 'classnames';
|
||||
import AccountDeposit from '../deposit';
|
||||
import AccountOperList from '../../accountOper/pure/List.pc';
|
||||
import { AccountBookOutlined, ScheduleOutlined } from '@ant-design/icons';
|
||||
export default function Render(props) {
|
||||
const { account, depositMax, unfinishedPayId, onDepositPayId } = props.data;
|
||||
const { t, createDepositPay, setMessage } = props.methods;
|
||||
|
|
@ -17,7 +18,7 @@ export default function Render(props) {
|
|||
if (account) {
|
||||
const { total, avail, '#oakLegalActions': legalActions, accountOper$account: opers } = account;
|
||||
return (<>
|
||||
<Card className={Styles.card} title={t('title')} extra={<Flex gap="middle">
|
||||
<Card className={Styles.card} title={<span><AccountBookOutlined /> {t('title')}</span>} extra={<Flex gap="middle">
|
||||
{legalActions?.includes('deposit') && <Button type="primary" onClick={() => {
|
||||
if (unfinishedPayId) {
|
||||
setUfOpen(true);
|
||||
|
|
@ -50,10 +51,10 @@ export default function Render(props) {
|
|||
<span className={Styles.value}>{t('common::pay.symbol')} {CentToString(total - avail, 2)}</span>
|
||||
</div>
|
||||
</Flex>
|
||||
{opers?.length && (<>
|
||||
{!!opers?.length && (<>
|
||||
<Divider />
|
||||
<div className={Styles.oper}>
|
||||
<span className={Styles.title}>{t('history')}</span>
|
||||
<span className={Styles.title}><ScheduleOutlined /> {t('history')}</span>
|
||||
<AccountOperList accountOpers={opers} t={t}/>
|
||||
</div>
|
||||
</>)}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
|||
import { EntityDict } from '../../../oak-app-domain';
|
||||
export default function Render(props: WebComponentProps<EntityDict, 'accountOper', false, {
|
||||
accountOpers: RowWithActions<EntityDict, 'accountOper'>[];
|
||||
}>): null;
|
||||
}>): string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
export default function Render(props) {
|
||||
const { accountOpers } = props.data;
|
||||
const { t } = props.methods;
|
||||
return null;
|
||||
return '还没有实现,利用pure/List渲染,要支持滚动加载 todo';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export default OakComponent({
|
|||
refunded: 1,
|
||||
timeoutAt: 1,
|
||||
forbidRefundAt: 1,
|
||||
externalId: 1,
|
||||
orderId: 1,
|
||||
accountId: 1,
|
||||
account: {
|
||||
|
|
|
|||
|
|
@ -16,5 +16,11 @@
|
|||
"cc": {
|
||||
"title": "关闭支付",
|
||||
"content": "您确认关闭此次支付吗?"
|
||||
},
|
||||
"wechat": {
|
||||
"native": {
|
||||
"tips": "请用微信扫描二维码支付",
|
||||
"tips2": "虚拟的支付二维码,不用扫~"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,13 @@ import React from 'react';
|
|||
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
import { OfflinePayConfig, PayConfig } from '../../../types/PayConfig';
|
||||
export declare function RenderOffline(pay: RowWithActions<EntityDict, 'pay'>, t: (key: string) => string, offline: OfflinePayConfig, updateMeta: (meta: any) => void, metaUpdatable: boolean): React.JSX.Element;
|
||||
export declare function RenderOffline(props: {
|
||||
pay: RowWithActions<EntityDict, 'pay'>;
|
||||
t: (key: string) => string;
|
||||
offline: OfflinePayConfig;
|
||||
updateMeta: (meta: any) => void;
|
||||
metaUpdatable: boolean;
|
||||
}): React.JSX.Element;
|
||||
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
|
||||
pay?: RowWithActions<EntityDict, 'pay'>;
|
||||
application?: EntityDict['application']['Schema'];
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import React from 'react';
|
||||
import { Input, Tag, Card, Form, Descriptions, Alert, Select, Button, Modal } from 'antd';
|
||||
import { Input, Tag, Card, QRCode, Form, Descriptions, Alert, Select, Button, Modal } from 'antd';
|
||||
import { CentToString } from 'oak-domain/lib/utils/money';
|
||||
import Styles from './web.pc.module.less';
|
||||
import { PAY_CHANNEL_OFFLINE_NAME } from '../../../types/PayConfig';
|
||||
export function RenderOffline(pay, t, offline, updateMeta, metaUpdatable) {
|
||||
import { PAY_CHANNEL_OFFLINE_NAME, PAY_CHANNEL_WECHAT_APP_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_MP_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME } from '../../../types/PayConfig';
|
||||
export function RenderOffline(props) {
|
||||
const { pay, t, offline, updateMeta, metaUpdatable } = props;
|
||||
const { meta, iState } = pay;
|
||||
return (<>
|
||||
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 14 }} layout="horizontal" style={{ width: '100%', marginTop: 12 }}>
|
||||
|
|
@ -28,7 +29,25 @@ export function RenderOffline(pay, t, offline, updateMeta, metaUpdatable) {
|
|||
</Form>
|
||||
</>);
|
||||
}
|
||||
function RenderPayMeta(pay, application, t, payConfig, updateMeta) {
|
||||
function RenderWechatPay(props) {
|
||||
const { pay, t } = props;
|
||||
const { externalId, channel } = pay;
|
||||
switch (channel) {
|
||||
case PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||
return (<>
|
||||
<QRCode value={externalId} size={280}/>
|
||||
<div className={Styles.qrCodeTips}>
|
||||
{process.env.NODE_ENV === 'production' ?
|
||||
<Alert type="info" message={t('wechat.native.tips')}/> :
|
||||
<Alert type="warning" message={t('wechat.native.tips2')}/>}
|
||||
</div>
|
||||
</>);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function RenderPayMeta(props) {
|
||||
const { pay, application, t, payConfig, updateMeta } = props;
|
||||
const { iState, channel } = pay;
|
||||
if (['unpaid', 'paying'].includes(iState) && pay.applicationId !== application.id && channel !== PAY_CHANNEL_OFFLINE_NAME) {
|
||||
return <Alert type='warning' message={t('notSameApp')}/>;
|
||||
|
|
@ -41,9 +60,22 @@ function RenderPayMeta(pay, application, t, payConfig, updateMeta) {
|
|||
&& ele.attrs?.includes('meta'));
|
||||
return (<>
|
||||
{iState === 'paying' && <Alert type='info' message={t('offline.tips')}/>}
|
||||
{RenderOffline(pay, t, payConfig.find(ele => ele.channel === PAY_CHANNEL_OFFLINE_NAME), updateMeta, metaUpdatable)}
|
||||
{RenderOffline({
|
||||
pay,
|
||||
t,
|
||||
offline: payConfig.find(ele => ele.channel === PAY_CHANNEL_OFFLINE_NAME),
|
||||
updateMeta,
|
||||
metaUpdatable
|
||||
})}
|
||||
</>);
|
||||
}
|
||||
case PAY_CHANNEL_WECHAT_APP_NAME:
|
||||
case PAY_CHANNEL_WECHAT_H5_NAME:
|
||||
case PAY_CHANNEL_WECHAT_JS_NAME:
|
||||
case PAY_CHANNEL_WECHAT_MP_NAME:
|
||||
case PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||
return <RenderWechatPay pay={pay} t={t}/>;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -70,7 +102,7 @@ export default function Render(props) {
|
|||
]}/>
|
||||
</div>
|
||||
<div className={Styles.oper}>
|
||||
{RenderPayMeta(pay, application, t, payConfig, (meta) => update({ meta }))}
|
||||
<RenderPayMeta pay={pay} t={t} application={application} payConfig={payConfig} updateMeta={(meta) => update({ meta })}/>
|
||||
</div>
|
||||
<div className={Styles.btn}>
|
||||
{oakExecutable === true && (<Button onClick={() => execute()}>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@
|
|||
margin-top: 40px;
|
||||
width: 60%;
|
||||
max-width: 600px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.qrCodeTips {
|
||||
margin-top: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
|
|
|
|||
|
|
@ -98,9 +98,9 @@ export default function Render(props) {
|
|||
<div className={Styles.spCon}>
|
||||
<Alert type="warning" message={t('csp.tips')}/>
|
||||
<Divider style={{ width: '80%', alignSelf: 'flex-end' }}/>
|
||||
{RenderOffline(spRow, t, offlineConfig, (meta) => {
|
||||
<RenderOffline pay={spRow} t={t} offline={offlineConfig} metaUpdatable={true} updateMeta={(meta) => {
|
||||
updateItem({ meta }, spRow.id, 'succeedPaying');
|
||||
}, true)}
|
||||
}}/>
|
||||
<div className={Styles.btn}>
|
||||
<Button type="primary" onClick={async () => {
|
||||
await execute();
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import React, { useState } from 'react';
|
|||
import { Button, Row, Tabs } from 'antd';
|
||||
import Styles from './web.pc.module.less';
|
||||
import PayConfigUpsert from '../upsert';
|
||||
import { PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_MP_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME } from '../../../types/PayConfig';
|
||||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||
import { AppTypeToPayChannelDict } from '../../../utils/payClazz';
|
||||
const SystemPayChannels = []; // [label, value]
|
||||
const ApplicationPayChannels = {};
|
||||
/**
|
||||
|
|
@ -26,18 +26,13 @@ export function registerApplicationPayChannel(type, channel) {
|
|||
ApplicationPayChannels[type] = [channel];
|
||||
}
|
||||
}
|
||||
const DefaultApplicationPayChannels = {
|
||||
web: [PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME],
|
||||
wechatMp: [PAY_CHANNEL_WECHAT_MP_NAME],
|
||||
wechatPublic: [PAY_CHANNEL_WECHAT_JS_NAME],
|
||||
};
|
||||
export default function render(props) {
|
||||
const { system, oakFullpath, operation, oakDirty } = props.data;
|
||||
const { t, update, setMessage, execute } = props.methods;
|
||||
const defaultApplicationPayChannelDict = {
|
||||
web: DefaultApplicationPayChannels.web.map(ele => [t(`payChannel::${ele}`), ele]),
|
||||
wechatMp: DefaultApplicationPayChannels.wechatMp.map(ele => [t(`payChannel::${ele}`), ele]),
|
||||
wechatPublic: DefaultApplicationPayChannels.wechatPublic.map(ele => [t(`payChannel::${ele}`), ele]),
|
||||
web: AppTypeToPayChannelDict.web.map(ele => [t(`payChannel::internal.${ele}`), ele]),
|
||||
wechatMp: AppTypeToPayChannelDict.wechatMp.map(ele => [t(`payChannel::internal.${ele}`), ele]),
|
||||
wechatPublic: AppTypeToPayChannelDict.wechatPublic.map(ele => [t(`payChannel::internal.${ele}`), ele]),
|
||||
};
|
||||
const [key, setKey] = useState('');
|
||||
if (system && oakFullpath) {
|
||||
|
|
@ -54,7 +49,7 @@ export default function render(props) {
|
|||
{t('system')}
|
||||
</div>),
|
||||
key: 'system',
|
||||
children: (<PayConfigUpsert key="system" config={payConfig} update={(config) => update({ payConfig: config })} channels={SystemPayChannels.concat([[t('payChannel::ACCOUNT'), 'ACCOUNT'], [t('payChannel::OFFLINE'), 'OFFLINE']])} t={t}/>),
|
||||
children: (<PayConfigUpsert key="system" config={payConfig} update={(config) => update({ payConfig: config })} channels={SystemPayChannels.concat([[t('payChannel::internal.ACCOUNT'), 'ACCOUNT'], [t('payChannel::internal.OFFLINE'), 'OFFLINE']])} t={t}/>),
|
||||
},
|
||||
{
|
||||
label: (<div className={Styles.padding}>
|
||||
|
|
|
|||
|
|
@ -81,6 +81,12 @@ const i18ns = [
|
|||
"cc": {
|
||||
"title": "关闭支付",
|
||||
"content": "您确认关闭此次支付吗?"
|
||||
},
|
||||
"wechat": {
|
||||
"native": {
|
||||
"tips": "请用微信扫描二维码支付",
|
||||
"tips2": "虚拟的支付二维码,不用扫~"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -252,7 +258,16 @@ const i18ns = [
|
|||
"WECHAT_MP": "微信支付",
|
||||
"WECHAT_NATIVE": "微信支付",
|
||||
"WECHAT_H5": "微信支付",
|
||||
"WECHAT_APP": "微信支付"
|
||||
"WECHAT_APP": "微信支付",
|
||||
"internal": {
|
||||
"ACCOUNT": "系统帐户",
|
||||
"OFFLINE": "线下支付",
|
||||
"WECHAT_JS": "微信支付JS",
|
||||
"WECHAT_MP": "微信支付小程序",
|
||||
"WECHAT_NATIVE": "微信支付NATIVE(二维码)",
|
||||
"WECHAT_H5": "微信支付H5",
|
||||
"WECHAT_APP": "微信支付APP"
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -5,5 +5,14 @@
|
|||
"WECHAT_MP": "微信支付",
|
||||
"WECHAT_NATIVE": "微信支付",
|
||||
"WECHAT_H5": "微信支付",
|
||||
"WECHAT_APP": "微信支付"
|
||||
"WECHAT_APP": "微信支付",
|
||||
"internal": {
|
||||
"ACCOUNT": "系统帐户",
|
||||
"OFFLINE": "线下支付",
|
||||
"WECHAT_JS": "微信支付JS",
|
||||
"WECHAT_MP": "微信支付小程序",
|
||||
"WECHAT_NATIVE": "微信支付NATIVE(二维码)",
|
||||
"WECHAT_H5": "微信支付H5",
|
||||
"WECHAT_APP": "微信支付APP"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { OpSchema, UpdateOperationData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { WechatPayChannel, WechatPayConfig } from "../../types/PayConfig";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
export default class WechatPay implements PayClazz {
|
||||
channel: string;
|
||||
constructor(channel: WechatPayChannel, appId: string, config: WechatPayConfig);
|
||||
prepay(pay: OpSchema, data: UpdateOperationData, context: BRC): Promise<void>;
|
||||
getState(pay: OpSchema): Promise<string | null | undefined>;
|
||||
close(pay: OpSchema): Promise<void>;
|
||||
decodeNotification(params: Record<string, string>, body: any): Promise<{
|
||||
payId: string;
|
||||
iState: string | null | undefined;
|
||||
extra?: any;
|
||||
answer: string;
|
||||
}[]>;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { EntityDict } from "../../oak-app-domain";
|
||||
import { OpSchema, UpdateOperationData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { WechatPayChannel, WechatPayConfig } from "../../types/PayConfig";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
export declare function registerGetPayStateResult(payState: NonNullable<EntityDict['pay']['OpSchema']['iState']>): void;
|
||||
export default class WechatPay implements PayClazz {
|
||||
channel: string;
|
||||
constructor(channel: WechatPayChannel, appId: string, config: WechatPayConfig);
|
||||
/**
|
||||
* 参照微信支付prepay接口模拟返回
|
||||
* @param pay
|
||||
* @param data
|
||||
* @param context
|
||||
*/
|
||||
prepay(pay: OpSchema, data: UpdateOperationData, context: BRC): Promise<void>;
|
||||
getState(pay: OpSchema): Promise<string | null | undefined>;
|
||||
close(pay: OpSchema): Promise<void>;
|
||||
decodeNotification(params: Record<string, string>, body: any): Promise<{
|
||||
payId: string;
|
||||
iState: string | null | undefined;
|
||||
extra?: any;
|
||||
answer: string;
|
||||
}[]>;
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import { PAY_CHANNEL_WECHAT_APP_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_MP_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME } from "../../types/PayConfig";
|
||||
let _PAY_STATE = '';
|
||||
export function registerGetPayStateResult(payState) {
|
||||
_PAY_STATE = payState;
|
||||
}
|
||||
export default class WechatPay {
|
||||
channel;
|
||||
constructor(channel, appId, config) {
|
||||
// todo
|
||||
this.channel = channel;
|
||||
}
|
||||
/**
|
||||
* 参照微信支付prepay接口模拟返回
|
||||
* @param pay
|
||||
* @param data
|
||||
* @param context
|
||||
*/
|
||||
async prepay(pay, data, context) {
|
||||
switch (this.channel) {
|
||||
case PAY_CHANNEL_WECHAT_APP_NAME:
|
||||
case PAY_CHANNEL_WECHAT_JS_NAME:
|
||||
case PAY_CHANNEL_WECHAT_MP_NAME: {
|
||||
const prepayId = Math.random().toString();
|
||||
data.externalId = prepayId;
|
||||
data.meta = {
|
||||
prepayId,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case PAY_CHANNEL_WECHAT_H5_NAME: {
|
||||
const h5Url = Math.random().toString();
|
||||
data.externalId = h5Url;
|
||||
data.meta = {
|
||||
h5Url,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||
const codeUrl = Math.random().toString();
|
||||
data.externalId = codeUrl;
|
||||
data.meta = {
|
||||
codeUrl,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
;
|
||||
data.timeoutAt = Date.now() + 7000 * 1000; // 微信官方文档过期时间2小时,提前一点
|
||||
}
|
||||
async getState(pay) {
|
||||
if (_PAY_STATE) {
|
||||
return _PAY_STATE;
|
||||
}
|
||||
const r = Math.random();
|
||||
if (r < 0.3) {
|
||||
return 'paying';
|
||||
}
|
||||
else if (r > 0.95) {
|
||||
return 'closed';
|
||||
}
|
||||
return 'paid';
|
||||
}
|
||||
async close(pay) {
|
||||
}
|
||||
decodeNotification(params, body) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
export default class WechatPay {
|
||||
channel;
|
||||
constructor(channel, appId, config) {
|
||||
// todo
|
||||
this.channel = channel;
|
||||
}
|
||||
prepay(pay, data, context) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getState(pay) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
close(pay) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
decodeNotification(params, body) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
import { EntityDict } from '../../oak-app-domain';
|
||||
import PayClazz from '../../types/PayClazz';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
export declare const AppTypeToPayChannelDict: {
|
||||
[k in EntityDict['application']['OpSchema']['type']]: string[];
|
||||
};
|
||||
type PayClazzConstructor = <ED extends EntityDict>(application: ED['application']['Schema'], channel: string, context: BRC) => Promise<PayClazz>;
|
||||
export declare function registerAppPayClazzConstructor(channel: string, constructor: PayClazzConstructor): void;
|
||||
export declare function getPayClazz(appId: string, channel: string, context: BRC): Promise<PayClazz>;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,49 @@
|
|||
import { PAY_CHANNEL_ACCOUNT_NAME, PAY_CHANNEL_OFFLINE_NAME } from "../../types/PayConfig";
|
||||
import { PAY_CHANNEL_ACCOUNT_NAME, PAY_CHANNEL_OFFLINE_NAME, PAY_CHANNEL_WECHAT_APP_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_MP_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME } from "../../types/PayConfig";
|
||||
import assert from 'assert';
|
||||
import Offline from './Offline';
|
||||
import Account from './Account';
|
||||
import WechatPay from './WechatPay';
|
||||
export const AppTypeToPayChannelDict = {
|
||||
web: [PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME],
|
||||
wechatMp: [PAY_CHANNEL_WECHAT_MP_NAME],
|
||||
wechatPublic: [PAY_CHANNEL_WECHAT_JS_NAME],
|
||||
native: [PAY_CHANNEL_WECHAT_APP_NAME],
|
||||
};
|
||||
async function createPayClazz(application, channel, context) {
|
||||
const { type, config, payConfig } = application;
|
||||
assert(AppTypeToPayChannelDict[type].includes(channel));
|
||||
const wechatPayConfig = (payConfig?.find(ele => ele.channel === channel));
|
||||
assert(wechatPayConfig);
|
||||
const channel2 = channel; // 目前应该只有wechat系的支付
|
||||
switch (type) {
|
||||
case 'web': {
|
||||
const { wechat } = config;
|
||||
assert(wechat);
|
||||
const appId = wechat.appId;
|
||||
return new WechatPay(channel2, appId, wechatPayConfig);
|
||||
}
|
||||
case 'wechatMp': {
|
||||
const { appId } = config;
|
||||
return new WechatPay(channel2, appId, wechatPayConfig);
|
||||
}
|
||||
case 'wechatPublic': {
|
||||
const { appId } = config;
|
||||
return new WechatPay(channel2, appId, wechatPayConfig);
|
||||
}
|
||||
default: {
|
||||
assert(false, '暂时不支持');
|
||||
}
|
||||
}
|
||||
}
|
||||
const PayChannelDict = {};
|
||||
const PayClazzConstructorDict = {
|
||||
[PAY_CHANNEL_OFFLINE_NAME]: async () => new Offline(),
|
||||
[PAY_CHANNEL_ACCOUNT_NAME]: async () => new Account(),
|
||||
[PAY_CHANNEL_WECHAT_APP_NAME]: createPayClazz,
|
||||
[PAY_CHANNEL_WECHAT_H5_NAME]: createPayClazz,
|
||||
[PAY_CHANNEL_WECHAT_JS_NAME]: createPayClazz,
|
||||
[PAY_CHANNEL_WECHAT_MP_NAME]: createPayClazz,
|
||||
[PAY_CHANNEL_WECHAT_NATIVE_NAME]: createPayClazz,
|
||||
};
|
||||
export function registerAppPayClazzConstructor(channel, constructor) {
|
||||
PayClazzConstructorDict[channel] = constructor;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { getPayClazz } from '../utils/payClazz';
|
|||
import assert from 'assert';
|
||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { mergeOperationResult } from 'oak-domain/lib/utils/operationResult';
|
||||
const QUERY_PAYING_STATE_GAP = 3600 * 1000;
|
||||
const QUERY_PAYING_STATE_GAP = process.env.NODE_ENV === 'production' ? 3600 * 1000 : 60 * 1000;
|
||||
const watchers = [
|
||||
{
|
||||
name: '对paying状态的订单,同步其真实支付状态',
|
||||
|
|
|
|||
|
|
@ -83,6 +83,12 @@ const i18ns = [
|
|||
"cc": {
|
||||
"title": "关闭支付",
|
||||
"content": "您确认关闭此次支付吗?"
|
||||
},
|
||||
"wechat": {
|
||||
"native": {
|
||||
"tips": "请用微信扫描二维码支付",
|
||||
"tips2": "虚拟的支付二维码,不用扫~"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -254,7 +260,16 @@ const i18ns = [
|
|||
"WECHAT_MP": "微信支付",
|
||||
"WECHAT_NATIVE": "微信支付",
|
||||
"WECHAT_H5": "微信支付",
|
||||
"WECHAT_APP": "微信支付"
|
||||
"WECHAT_APP": "微信支付",
|
||||
"internal": {
|
||||
"ACCOUNT": "系统帐户",
|
||||
"OFFLINE": "线下支付",
|
||||
"WECHAT_JS": "微信支付JS",
|
||||
"WECHAT_MP": "微信支付小程序",
|
||||
"WECHAT_NATIVE": "微信支付NATIVE(二维码)",
|
||||
"WECHAT_H5": "微信支付H5",
|
||||
"WECHAT_APP": "微信支付APP"
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -5,5 +5,14 @@
|
|||
"WECHAT_MP": "微信支付",
|
||||
"WECHAT_NATIVE": "微信支付",
|
||||
"WECHAT_H5": "微信支付",
|
||||
"WECHAT_APP": "微信支付"
|
||||
"WECHAT_APP": "微信支付",
|
||||
"internal": {
|
||||
"ACCOUNT": "系统帐户",
|
||||
"OFFLINE": "线下支付",
|
||||
"WECHAT_JS": "微信支付JS",
|
||||
"WECHAT_MP": "微信支付小程序",
|
||||
"WECHAT_NATIVE": "微信支付NATIVE(二维码)",
|
||||
"WECHAT_H5": "微信支付H5",
|
||||
"WECHAT_APP": "微信支付APP"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { OpSchema, UpdateOperationData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { WechatPayChannel, WechatPayConfig } from "../../types/PayConfig";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
export default class WechatPay implements PayClazz {
|
||||
channel: string;
|
||||
constructor(channel: WechatPayChannel, appId: string, config: WechatPayConfig);
|
||||
prepay(pay: OpSchema, data: UpdateOperationData, context: BRC): Promise<void>;
|
||||
getState(pay: OpSchema): Promise<string | null | undefined>;
|
||||
close(pay: OpSchema): Promise<void>;
|
||||
decodeNotification(params: Record<string, string>, body: any): Promise<{
|
||||
payId: string;
|
||||
iState: string | null | undefined;
|
||||
extra?: any;
|
||||
answer: string;
|
||||
}[]>;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { EntityDict } from "../../oak-app-domain";
|
||||
import { OpSchema, UpdateOperationData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { WechatPayChannel, WechatPayConfig } from "../../types/PayConfig";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
export declare function registerGetPayStateResult(payState: NonNullable<EntityDict['pay']['OpSchema']['iState']>): void;
|
||||
export default class WechatPay implements PayClazz {
|
||||
channel: string;
|
||||
constructor(channel: WechatPayChannel, appId: string, config: WechatPayConfig);
|
||||
/**
|
||||
* 参照微信支付prepay接口模拟返回
|
||||
* @param pay
|
||||
* @param data
|
||||
* @param context
|
||||
*/
|
||||
prepay(pay: OpSchema, data: UpdateOperationData, context: BRC): Promise<void>;
|
||||
getState(pay: OpSchema): Promise<string | null | undefined>;
|
||||
close(pay: OpSchema): Promise<void>;
|
||||
decodeNotification(params: Record<string, string>, body: any): Promise<{
|
||||
payId: string;
|
||||
iState: string | null | undefined;
|
||||
extra?: any;
|
||||
answer: string;
|
||||
}[]>;
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.registerGetPayStateResult = void 0;
|
||||
const PayConfig_1 = require("../../types/PayConfig");
|
||||
let _PAY_STATE = '';
|
||||
function registerGetPayStateResult(payState) {
|
||||
_PAY_STATE = payState;
|
||||
}
|
||||
exports.registerGetPayStateResult = registerGetPayStateResult;
|
||||
class WechatPay {
|
||||
channel;
|
||||
constructor(channel, appId, config) {
|
||||
// todo
|
||||
this.channel = channel;
|
||||
}
|
||||
/**
|
||||
* 参照微信支付prepay接口模拟返回
|
||||
* @param pay
|
||||
* @param data
|
||||
* @param context
|
||||
*/
|
||||
async prepay(pay, data, context) {
|
||||
switch (this.channel) {
|
||||
case PayConfig_1.PAY_CHANNEL_WECHAT_APP_NAME:
|
||||
case PayConfig_1.PAY_CHANNEL_WECHAT_JS_NAME:
|
||||
case PayConfig_1.PAY_CHANNEL_WECHAT_MP_NAME: {
|
||||
const prepayId = Math.random().toString();
|
||||
data.externalId = prepayId;
|
||||
data.meta = {
|
||||
prepayId,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case PayConfig_1.PAY_CHANNEL_WECHAT_H5_NAME: {
|
||||
const h5Url = Math.random().toString();
|
||||
data.externalId = h5Url;
|
||||
data.meta = {
|
||||
h5Url,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case PayConfig_1.PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||
const codeUrl = Math.random().toString();
|
||||
data.externalId = codeUrl;
|
||||
data.meta = {
|
||||
codeUrl,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
;
|
||||
data.timeoutAt = Date.now() + 7000 * 1000; // 微信官方文档过期时间2小时,提前一点
|
||||
}
|
||||
async getState(pay) {
|
||||
if (_PAY_STATE) {
|
||||
return _PAY_STATE;
|
||||
}
|
||||
const r = Math.random();
|
||||
if (r < 0.3) {
|
||||
return 'paying';
|
||||
}
|
||||
else if (r > 0.95) {
|
||||
return 'closed';
|
||||
}
|
||||
return 'paid';
|
||||
}
|
||||
async close(pay) {
|
||||
}
|
||||
decodeNotification(params, body) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
exports.default = WechatPay;
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class WechatPay {
|
||||
channel;
|
||||
constructor(channel, appId, config) {
|
||||
// todo
|
||||
this.channel = channel;
|
||||
}
|
||||
prepay(pay, data, context) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getState(pay) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
close(pay) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
decodeNotification(params, body) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
exports.default = WechatPay;
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
import { EntityDict } from '../../oak-app-domain';
|
||||
import PayClazz from '../../types/PayClazz';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
export declare const AppTypeToPayChannelDict: {
|
||||
[k in EntityDict['application']['OpSchema']['type']]: string[];
|
||||
};
|
||||
type PayClazzConstructor = <ED extends EntityDict>(application: ED['application']['Schema'], channel: string, context: BRC) => Promise<PayClazz>;
|
||||
export declare function registerAppPayClazzConstructor(channel: string, constructor: PayClazzConstructor): void;
|
||||
export declare function getPayClazz(appId: string, channel: string, context: BRC): Promise<PayClazz>;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,53 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getPayClazz = exports.registerAppPayClazzConstructor = void 0;
|
||||
exports.getPayClazz = exports.registerAppPayClazzConstructor = exports.AppTypeToPayChannelDict = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const PayConfig_1 = require("../../types/PayConfig");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const Offline_1 = tslib_1.__importDefault(require("./Offline"));
|
||||
const Account_1 = tslib_1.__importDefault(require("./Account"));
|
||||
const WechatPay_1 = tslib_1.__importDefault(require("./WechatPay"));
|
||||
exports.AppTypeToPayChannelDict = {
|
||||
web: [PayConfig_1.PAY_CHANNEL_WECHAT_JS_NAME, PayConfig_1.PAY_CHANNEL_WECHAT_H5_NAME, PayConfig_1.PAY_CHANNEL_WECHAT_NATIVE_NAME],
|
||||
wechatMp: [PayConfig_1.PAY_CHANNEL_WECHAT_MP_NAME],
|
||||
wechatPublic: [PayConfig_1.PAY_CHANNEL_WECHAT_JS_NAME],
|
||||
native: [PayConfig_1.PAY_CHANNEL_WECHAT_APP_NAME],
|
||||
};
|
||||
async function createPayClazz(application, channel, context) {
|
||||
const { type, config, payConfig } = application;
|
||||
(0, assert_1.default)(exports.AppTypeToPayChannelDict[type].includes(channel));
|
||||
const wechatPayConfig = (payConfig?.find(ele => ele.channel === channel));
|
||||
(0, assert_1.default)(wechatPayConfig);
|
||||
const channel2 = channel; // 目前应该只有wechat系的支付
|
||||
switch (type) {
|
||||
case 'web': {
|
||||
const { wechat } = config;
|
||||
(0, assert_1.default)(wechat);
|
||||
const appId = wechat.appId;
|
||||
return new WechatPay_1.default(channel2, appId, wechatPayConfig);
|
||||
}
|
||||
case 'wechatMp': {
|
||||
const { appId } = config;
|
||||
return new WechatPay_1.default(channel2, appId, wechatPayConfig);
|
||||
}
|
||||
case 'wechatPublic': {
|
||||
const { appId } = config;
|
||||
return new WechatPay_1.default(channel2, appId, wechatPayConfig);
|
||||
}
|
||||
default: {
|
||||
(0, assert_1.default)(false, '暂时不支持');
|
||||
}
|
||||
}
|
||||
}
|
||||
const PayChannelDict = {};
|
||||
const PayClazzConstructorDict = {
|
||||
[PayConfig_1.PAY_CHANNEL_OFFLINE_NAME]: async () => new Offline_1.default(),
|
||||
[PayConfig_1.PAY_CHANNEL_ACCOUNT_NAME]: async () => new Account_1.default(),
|
||||
[PayConfig_1.PAY_CHANNEL_WECHAT_APP_NAME]: createPayClazz,
|
||||
[PayConfig_1.PAY_CHANNEL_WECHAT_H5_NAME]: createPayClazz,
|
||||
[PayConfig_1.PAY_CHANNEL_WECHAT_JS_NAME]: createPayClazz,
|
||||
[PayConfig_1.PAY_CHANNEL_WECHAT_MP_NAME]: createPayClazz,
|
||||
[PayConfig_1.PAY_CHANNEL_WECHAT_NATIVE_NAME]: createPayClazz,
|
||||
};
|
||||
function registerAppPayClazzConstructor(channel, constructor) {
|
||||
PayClazzConstructorDict[channel] = constructor;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const payClazz_1 = require("../utils/payClazz");
|
|||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const operationResult_1 = require("oak-domain/lib/utils/operationResult");
|
||||
const QUERY_PAYING_STATE_GAP = 3600 * 1000;
|
||||
const QUERY_PAYING_STATE_GAP = process.env.NODE_ENV === 'production' ? 3600 * 1000 : 60 * 1000;
|
||||
const watchers = [
|
||||
{
|
||||
name: '对paying状态的订单,同步其真实支付状态',
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'account', f
|
|||
</div>
|
||||
</Flex>
|
||||
{
|
||||
opers?.length && (
|
||||
!!opers?.length && (
|
||||
<>
|
||||
<Divider />
|
||||
<div className={Styles.oper}>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export default OakComponent({
|
|||
refunded: 1,
|
||||
timeoutAt: 1,
|
||||
forbidRefundAt: 1,
|
||||
externalId: 1,
|
||||
orderId: 1,
|
||||
accountId: 1,
|
||||
account: {
|
||||
|
|
|
|||
|
|
@ -16,5 +16,11 @@
|
|||
"cc": {
|
||||
"title": "关闭支付",
|
||||
"content": "您确认关闭此次支付吗?"
|
||||
},
|
||||
"wechat": {
|
||||
"native": {
|
||||
"tips": "请用微信扫描二维码支付",
|
||||
"tips2": "虚拟的支付二维码,不用扫~"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,13 @@
|
|||
margin-top: 40px;
|
||||
width: 60%;
|
||||
max-width: 600px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.qrCodeTips {
|
||||
margin-top: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Input, Radio, Space, Tag, Card, Flex, Form, Descriptions, Typography, Alert, Select, Button, Modal } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Input, Space, Tag, Card, QRCode, Form, Descriptions, Typography, Alert, Select, Button, Modal } from 'antd';
|
||||
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
||||
import { EntityDict } from '@project/oak-app-domain';
|
||||
import { CentToString } from 'oak-domain/lib/utils/money';
|
||||
import { Detail } from '../../AbstractComponents';
|
||||
import Styles from './web.pc.module.less';
|
||||
import classNames from 'classnames';
|
||||
import * as dayJs from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
dayJs.extend(duration);
|
||||
import {
|
||||
OfflinePayConfig,
|
||||
PAY_CHANNEL_ACCOUNT_NAME, PAY_CHANNEL_OFFLINE_NAME, PAY_CHANNEL_WECHAT_APP_NAME,
|
||||
|
|
@ -14,13 +17,14 @@ import {
|
|||
} from '../../../types/PayConfig';
|
||||
import { WechatOutlined, MoneyCollectOutlined, WalletOutlined } from '@ant-design/icons';
|
||||
|
||||
export function RenderOffline(
|
||||
export function RenderOffline(props: {
|
||||
pay: RowWithActions<EntityDict, 'pay'>,
|
||||
t: (key: string) => string,
|
||||
offline: OfflinePayConfig,
|
||||
updateMeta: (meta: any) => void,
|
||||
metaUpdatable: boolean
|
||||
) {
|
||||
}) {
|
||||
const { pay, t, offline, updateMeta, metaUpdatable } = props;
|
||||
const { meta, iState } = pay;
|
||||
return (
|
||||
<>
|
||||
|
|
@ -65,13 +69,67 @@ export function RenderOffline(
|
|||
)
|
||||
}
|
||||
|
||||
function RenderPayMeta(
|
||||
function Counter(props: { deadline: number }) {
|
||||
const { deadline } = props;
|
||||
const [counter, setCounter] = useState('');
|
||||
const timerFn = () => {
|
||||
const now = Date.now();
|
||||
if (now < deadline) {
|
||||
const duration = dayJs.duration(deadline - now);
|
||||
setCounter(duration.format('HH:mm:ss'));
|
||||
setTimeout(timerFn, 1000);
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
timerFn();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Typography.Title level={3}>{counter}</Typography.Title>
|
||||
);
|
||||
}
|
||||
|
||||
function RenderWechatPay(props: {
|
||||
pay: RowWithActions<EntityDict, 'pay'>;
|
||||
t: (key: string) => string;
|
||||
}) {
|
||||
const { pay, t } = props;
|
||||
const { externalId, channel, timeoutAt, iState } = pay;
|
||||
switch (channel) {
|
||||
case PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||
if (iState! === 'paying') {
|
||||
return (
|
||||
<>
|
||||
<Counter deadline={timeoutAt as number} />
|
||||
<QRCode
|
||||
value={externalId!}
|
||||
size={280}
|
||||
/>
|
||||
<div className={Styles.qrCodeTips}>
|
||||
{
|
||||
process.env.NODE_ENV === 'production' ?
|
||||
<Alert type="info" message={t('wechat.native.tips')} /> :
|
||||
<Alert type="warning" message={t('wechat.native.tips2')} />
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function RenderPayMeta(props: {
|
||||
pay: RowWithActions<EntityDict, 'pay'>,
|
||||
application: EntityDict['application']['Schema'],
|
||||
t: (key: string) => string,
|
||||
payConfig: PayConfig,
|
||||
updateMeta: (meta: any) => void,
|
||||
) {
|
||||
}) {
|
||||
const { pay, application, t, payConfig, updateMeta } = props;
|
||||
const { iState, channel } = pay;
|
||||
if (['unpaid', 'paying'].includes(iState!) && pay.applicationId !== application.id && channel !== PAY_CHANNEL_OFFLINE_NAME) {
|
||||
return <Alert type='warning' message={t('notSameApp')} />
|
||||
|
|
@ -88,15 +146,24 @@ function RenderPayMeta(
|
|||
<>
|
||||
{iState === 'paying' && <Alert type='info' message={t('offline.tips')} />}
|
||||
{RenderOffline(
|
||||
pay,
|
||||
t,
|
||||
payConfig.find(ele => ele.channel === PAY_CHANNEL_OFFLINE_NAME) as OfflinePayConfig,
|
||||
updateMeta,
|
||||
metaUpdatable
|
||||
{
|
||||
pay,
|
||||
t,
|
||||
offline: payConfig.find(ele => ele.channel === PAY_CHANNEL_OFFLINE_NAME) as OfflinePayConfig,
|
||||
updateMeta,
|
||||
metaUpdatable
|
||||
}
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case PAY_CHANNEL_WECHAT_APP_NAME:
|
||||
case PAY_CHANNEL_WECHAT_H5_NAME:
|
||||
case PAY_CHANNEL_WECHAT_JS_NAME:
|
||||
case PAY_CHANNEL_WECHAT_MP_NAME:
|
||||
case PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||
return <RenderWechatPay pay={pay} t={t} />
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -140,7 +207,13 @@ export default function Render(props: WebComponentProps<EntityDict, 'pay', false
|
|||
/>
|
||||
</div>
|
||||
<div className={Styles.oper}>
|
||||
{RenderPayMeta(pay, application, t, payConfig!, (meta) => update({ meta }))}
|
||||
<RenderPayMeta
|
||||
pay={pay}
|
||||
t={t}
|
||||
application={application}
|
||||
payConfig={payConfig!}
|
||||
updateMeta={(meta) => update({ meta })}
|
||||
/>
|
||||
</div>
|
||||
<div className={Styles.btn}>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -133,11 +133,15 @@ export default function Render(props: WebComponentProps<EntityDict, 'pay', false
|
|||
<div className={Styles.spCon}>
|
||||
<Alert type="warning" message={t('csp.tips')} />
|
||||
<Divider style={{ width: '80%', alignSelf: 'flex-end' }} />
|
||||
{
|
||||
RenderOffline(spRow as RowWithActions<EntityDict, 'pay'>, t, offlineConfig, (meta) => {
|
||||
<RenderOffline
|
||||
pay={spRow as RowWithActions<EntityDict, 'pay'>}
|
||||
t={t}
|
||||
offline={offlineConfig}
|
||||
metaUpdatable={true}
|
||||
updateMeta={(meta) => {
|
||||
updateItem({ meta }, spRow!.id!, 'succeedPaying');
|
||||
}, true)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className={Styles.btn}>
|
||||
<Button
|
||||
type="primary"
|
||||
|
|
|
|||
|
|
@ -83,6 +83,12 @@ const i18ns: I18n[] = [
|
|||
"cc": {
|
||||
"title": "关闭支付",
|
||||
"content": "您确认关闭此次支付吗?"
|
||||
},
|
||||
"wechat": {
|
||||
"native": {
|
||||
"tips": "请用微信扫描二维码支付",
|
||||
"tips2": "虚拟的支付二维码,不用扫~"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -254,7 +260,16 @@ const i18ns: I18n[] = [
|
|||
"WECHAT_MP": "微信支付",
|
||||
"WECHAT_NATIVE": "微信支付",
|
||||
"WECHAT_H5": "微信支付",
|
||||
"WECHAT_APP": "微信支付"
|
||||
"WECHAT_APP": "微信支付",
|
||||
"internal": {
|
||||
"ACCOUNT": "系统帐户",
|
||||
"OFFLINE": "线下支付",
|
||||
"WECHAT_JS": "微信支付JS",
|
||||
"WECHAT_MP": "微信支付小程序",
|
||||
"WECHAT_NATIVE": "微信支付NATIVE(二维码)",
|
||||
"WECHAT_H5": "微信支付H5",
|
||||
"WECHAT_APP": "微信支付APP"
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { OpSchema, UpdateOperationData } from "@project/oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "@project/types/PayClazz";
|
||||
import { PAY_CHANNEL_ACCOUNT_NAME } from '@project/types/PayConfig';
|
||||
import { BRC } from "@project/types/RuntimeCxt";
|
||||
import { OpSchema, UpdateOperationData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { PAY_CHANNEL_ACCOUNT_NAME } from '../../types/PayConfig';
|
||||
import assert from 'assert';
|
||||
import { generateNewIdAsync } from "oak-domain/lib/utils/uuid";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { OpSchema, UpdateOperationData } from "@project/oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "@project/types/PayClazz";
|
||||
import { PAY_CHANNEL_OFFLINE_NAME } from "@project/types/PayConfig";
|
||||
import { BRC } from "@project/types/RuntimeCxt";
|
||||
import { OpSchema, UpdateOperationData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { PAY_CHANNEL_OFFLINE_NAME } from "../../types/PayConfig";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
import assert from "assert";
|
||||
|
||||
export default class Offline implements PayClazz {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,80 @@
|
|||
import { OpSchema, UpdateOperationData } from "@project/oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "@project/types/PayClazz";
|
||||
import { WechatPayChannel, WechatPayConfig } from "@project/types/PayConfig";
|
||||
import { BRC } from "@project/types/RuntimeCxt";
|
||||
import { EntityDict } from "../../oak-app-domain";
|
||||
import { OpSchema, UpdateOperationData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { PAY_CHANNEL_ACCOUNT_NAME, PAY_CHANNEL_WECHAT_APP_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_JS_NAME,
|
||||
PAY_CHANNEL_WECHAT_MP_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME, WechatPayChannel, WechatPayConfig } from "../../types/PayConfig";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
|
||||
let _PAY_STATE = '';
|
||||
export function registerGetPayStateResult(payState: NonNullable<EntityDict['pay']['OpSchema']['iState']>) {
|
||||
_PAY_STATE = payState;
|
||||
}
|
||||
|
||||
export default class WechatPay implements PayClazz {
|
||||
channel: string;
|
||||
channel: string;
|
||||
|
||||
|
||||
constructor(channel: WechatPayChannel, appId: string, config: WechatPayConfig) {
|
||||
// todo
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参照微信支付prepay接口模拟返回
|
||||
* @param pay
|
||||
* @param data
|
||||
* @param context
|
||||
*/
|
||||
async prepay(pay: OpSchema, data: UpdateOperationData, context: BRC): Promise<void> {
|
||||
switch (this.channel) {
|
||||
case PAY_CHANNEL_WECHAT_APP_NAME:
|
||||
case PAY_CHANNEL_WECHAT_JS_NAME:
|
||||
case PAY_CHANNEL_WECHAT_MP_NAME: {
|
||||
const prepayId = Math.random().toString();
|
||||
data.externalId = prepayId;
|
||||
data.meta = {
|
||||
prepayId,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case PAY_CHANNEL_WECHAT_H5_NAME: {
|
||||
const h5Url = Math.random().toString();
|
||||
data.externalId = h5Url;
|
||||
data.meta = {
|
||||
h5Url,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||
const codeUrl = Math.random().toString();
|
||||
data.externalId = codeUrl;
|
||||
data.meta = {
|
||||
codeUrl,
|
||||
};
|
||||
break;
|
||||
}
|
||||
};
|
||||
data.timeoutAt = Date.now() + 7000 * 1000; // 微信官方文档过期时间2小时,提前一点
|
||||
}
|
||||
async getState(pay: OpSchema): Promise<string | null | undefined> {
|
||||
if (_PAY_STATE) {
|
||||
return _PAY_STATE;
|
||||
}
|
||||
const r = Math.random();
|
||||
if (r < 0.3) {
|
||||
return 'paying';
|
||||
}
|
||||
else if (r > 0.95) {
|
||||
return 'closed';
|
||||
}
|
||||
|
||||
return 'paid';
|
||||
}
|
||||
async close(pay: OpSchema): Promise<void> {
|
||||
}
|
||||
|
||||
decodeNotification(params: Record<string, string>, body: any): Promise<{ payId: string; iState: string | null | undefined; extra?: any; answer: string; }[]> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
|
||||
constructor(channel: WechatPayChannel, appId: string, config: WechatPayConfig) {
|
||||
// todo
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
prepay(pay: OpSchema, data: UpdateOperationData, context: BRC): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getState(pay: OpSchema): Promise<string | null | undefined> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
close(pay: OpSchema): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
decodeNotification(params: Record<string, string>, body: any): Promise<{ payId: string; iState: string | null | undefined; extra?: any; answer: string; }[]> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
import { EntityDict } from '@project/oak-app-domain';
|
||||
import PayClazz from '@project/types/PayClazz';
|
||||
import { BRC } from '@project/types/RuntimeCxt';
|
||||
import { PAY_CHANNEL_ACCOUNT_NAME, PAY_CHANNEL_OFFLINE_NAME, PAY_CHANNEL_WECHAT_APP_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_MP_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME, WechatPayChannel, WechatPayConfig } from "@project/types/PayConfig";
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import PayClazz from '../../types/PayClazz';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
import { PAY_CHANNEL_ACCOUNT_NAME, PAY_CHANNEL_OFFLINE_NAME, PAY_CHANNEL_WECHAT_APP_NAME,
|
||||
PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_MP_NAME,
|
||||
PAY_CHANNEL_WECHAT_NATIVE_NAME, WechatPayChannel, WechatPayConfig } from "../../types/PayConfig";
|
||||
import assert from 'assert';
|
||||
|
||||
import Offline from './Offline';
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
|||
import { OperationResult } from 'oak-domain/lib/types';
|
||||
import { mergeOperationResult } from 'oak-domain/lib/utils/operationResult';
|
||||
|
||||
const QUERY_PAYING_STATE_GAP = 3600 * 1000;
|
||||
const QUERY_PAYING_STATE_GAP = process.env.NODE_ENV === 'production' ? 3600 * 1000 : 60 * 1000;
|
||||
|
||||
const watchers: Watcher<EntityDict, 'pay', BRC>[] = [
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue