oak-pay-business/es/components/pay/detail/index.js

406 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { CentToString } from "oak-domain/lib/utils/money";
import { DATA_SUBSCRIBER_KEYS } from "../../../config/constants";
import assert from "assert";
import { isWeiXin } from 'oak-frontend-base/es/utils/utils';
import { merge } from "oak-domain/lib/utils/lodash";
import { canStartPay } from "../../../utils/wpProductFrontend";
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import { StartPayFailure } from "../../../types/Exception";
const PayRoutineDict = {
wpProduct: {
projection: {
wpProduct: {
id: 1,
type: 1,
},
},
routine: async (pay, features) => {
const { iState, wpProduct, meta } = pay;
switch (wpProduct.type) {
case 'mp': {
if (process.env.OAK_PLATFORM === 'wechatMp') {
const { prepayMeta } = meta;
if (prepayMeta) {
try {
const result = await wx.requestPayment(prepayMeta);
process.env.NODE_ENV === 'development' && console.log(result);
}
catch (err) {
throw new StartPayFailure(err.errMsg.includes('cancel')
? features.locales.t('startPayError.userCancel')
: features.locales.t('startPayError.unknown'));
}
}
else {
throw new StartPayFailure(features.locales.t('startPayError.illegalPrepayData'));
}
}
else {
throw new StartPayFailure(features.locales.t('startPayError.falseEnv', { env: 'wechatMp' }));
}
return;
}
case 'jsapi': {
if (process.env.OAK_PLATFORM === 'web' && isWeiXin) {
const { prepayMeta } = meta;
if (prepayMeta) {
const { timeStamp, ...rest } = prepayMeta;
// chooseWXPay文档都找不到了网上查出来这里timestamp的s要小写吐血了
await features.wechatSdk.loadWxAPi('chooseWXPay', {
timestamp: timeStamp,
...rest,
});
}
else {
throw new StartPayFailure(features.locales.t('startPayError.illegalPrepayData'));
}
}
}
default: {
assert('尚未实现');
}
}
},
judgeCanPay: canStartPay,
}
};
export function registerFrontendPayRoutine(entity, routine, projection, judgeCanPay) {
PayRoutineDict[entity] = {
routine,
projection,
judgeCanPay,
};
}
export default OakComponent({
entity: 'pay',
isList: false,
projection: () => {
const baseProjection = {
id: 1,
applicationId: 1,
price: 1,
meta: 1,
iState: 1,
paid: 1,
refunded: 1,
timeoutAt: 1,
forbidRefundAt: 1,
externalId: 1,
orderId: 1,
depositId: 1,
deposit: {
id: 1,
accountId: 1,
price: 1,
loss: 1,
iState: 1,
shipId: 1,
},
order: {
id: 1,
creatorId: 1,
},
entity: 1,
entityId: 1,
creatorId: 1,
phantom3: 1,
};
for (const k in PayRoutineDict) {
merge(baseProjection, PayRoutineDict[k].projection);
}
return baseProjection;
},
properties: {
onClose: () => undefined,
onPaid: () => undefined,
onPayFailure: () => undefined,
goBackable: false,
disableAutoPay: false,
closeWhenFailure: false,
mode: 'frontend',
autoSuccessAt: true,
disableClose: false,
},
data: {
showCloseConfirmMp: false,
showChannelSelectMp: false,
unsub: undefined,
unsubShip: undefined,
},
formData({ data, props }) {
const application = this.features.application.getApplication();
const iState = data?.iState;
let iStateStr = this.t(`pay:v.iState.${iState}`);
let iStateColor = iState && this.features.style.getColor('pay', 'iState', iState);
if (data?.depositId && data?.deposit?.shipId && data?.deposit?.iState === 'shipped') {
iStateStr = this.t('ship.shipped');
iStateColor = this.features.style.getColor('deposit', 'iState', data?.deposit?.iState);
}
const startPayable = iState === 'paying' && !['account', 'offlineAccount'].includes(data.entity) && (PayRoutineDict[data.entity] && PayRoutineDict[data.entity].judgeCanPay(data, this.features));
const { mode } = this.props;
const succeedable = data["#oakLegalActions"]?.includes('succeedPaying');
const payChannels = this.features.pay.getPayChannels();
const offlines = this.features.cache.get('offlineAccount', {
data: {
id: 1,
type: 1,
channel: 1,
name: 1,
qrCode: 1,
},
filter: {
systemId: this.features.application.getApplication().systemId,
}
}).map(ele => {
const color = this.features.style.getColor('offlineAccount', 'type', ele.type);
return {
color,
...ele,
};
});
const offline = offlines?.find(ele => ele.id === data.entityId);
return {
type: data?.orderId ? 'order' : 'deposit',
pay: data,
application,
iStateColor,
closable: !props.disableClose && !!(data?.["#oakLegalActions"]?.includes('close')),
startPayable,
succeedable,
offline,
offlines,
notSameApp: data && data.applicationId !== application.id && data.entity !== 'offlineAccount',
priceStr: data?.price && CentToString(data.price, 2),
oakExecutable: this.tryExecute(),
iStateStr,
shipId: data?.deposit?.shipId,
depositId: data?.depositId,
};
},
features: [{
feature: 'application',
callback() {
this.refreshOfflineAccounts();
}
}],
actions: ['close', 'startPaying', {
action: 'update',
attrs: ['entityId'],
}, 'succeedPaying'],
methods: {
refreshOfflineAccounts() {
const { entity } = this.state.pay || {};
if (entity === 'offlineAccount') {
this.features.cache.refresh('offlineAccount', {
data: {
id: 1,
channel: 1,
name: 1,
type: 1,
qrCode: 1,
},
filter: {
systemId: this.features.application.getApplication().systemId,
}
});
}
},
succeed(successAt) {
const { pay } = this.state;
this.update({
successAt,
paid: pay.price,
});
return this.execute('succeedPaying');
},
executeMp() {
return this.execute();
},
resetMp() {
return this.clean();
},
closeMp() {
this.setState({
showCloseConfirmMp: true,
});
},
cancelCloseMp() {
this.setState({
showCloseConfirmMp: false,
});
},
async confirmCloseMp() {
await this.execute('close');
this.cancelCloseMp();
const { onClose } = this.props;
onClose && onClose();
},
goBack() {
this.navigateBack();
},
async startPay() {
const { onPaid, onClose, onPayFailure } = this.props;
const { pay } = this.state;
try {
await PayRoutineDict[pay.entity].routine(pay, this.features);
onPaid && onPaid();
}
catch (err) {
if (this.props.closeWhenFailure) {
await this.execute(undefined, undefined, undefined, [
{
entity: 'pay',
operation: {
id: await generateNewIdAsync(),
action: 'close',
data: {},
filter: {
id: this.props.oakId,
},
},
}
]);
onClose && onClose();
}
else {
onPayFailure && onPayFailure();
}
this.features.message.setMessage({
type: 'warning',
content: err.message,
});
}
},
openChannelSelectMp() {
this.setState({
showChannelSelectMp: true,
});
},
closeChannelSelectMp() {
this.setState({
showChannelSelectMp: false,
});
},
async updateOfflineIdMp(touch) {
const { detail } = touch;
const { currentKey } = detail;
const { oakId } = this.props;
if (currentKey) {
await this.execute(undefined, undefined, undefined, [
{
entity: 'pay',
operation: {
id: await generateNewIdAsync(),
action: 'update',
data: {
entity: 'offlineAccount',
entityId: currentKey,
},
filter: {
id: oakId,
},
}
}
]);
this.setState({
showChannelSelectMp: false,
});
}
},
updateExternalIdMp(input) {
const { detail } = input;
this.update({
externalId: detail.value,
});
},
clearExternalIdMp() {
this.update({
externalId: null,
});
}
},
lifetimes: {
async ready() {
const { oakId } = this.props;
assert(typeof oakId === 'string');
const unsub = await this.subDataEvents([`${DATA_SUBSCRIBER_KEYS.payStateChanged}-${oakId}`]);
this.setState({
unsub,
});
},
detached() {
const { unsub, unsubShip } = this.state;
unsub && unsub();
unsubShip && unsubShip();
},
},
listeners: {
startPayable(prev, next) {
if (next.startPayable && !this.props.disableAutoPay) {
this.startPay();
}
},
pay(prev, next) {
if (!prev.pay && next.pay) {
this.refreshOfflineAccounts();
}
},
async shipId(prev, next) {
const { depositId } = this.state;
if (depositId && prev.shipId !== next.shipId && next.shipId) {
if (prev.shipId) {
const { unsubShip } = this.state;
unsubShip && unsubShip();
}
const unsubShip = await this.subDataEvents([`${DATA_SUBSCRIBER_KEYS.shipStateChanged}-${next.shipId}`], async (event, opRecords) => {
if (process.env.OAK_PLATFORM === 'wechatMp') {
//在充值虚拟发货成功后,小程序唤起确认收货组件
for (const record of opRecords) {
if (record.a === 'u') {
const { e, d } = record;
const { pay } = this.state;
if (e === 'ship' && d.iState === 'ship' && pay.meta.transaction_id) {
if (wx.openBusinessView) {
wx.openBusinessView({
businessType: 'weappOrderConfirm',
extraData: {
transaction_id: pay.meta.transaction_id,
},
async success() {
console.log('success');
const mpShipState = await this.features.cache.exec('getMpShipState', {
depositId,
});
if (mpShipState === 'received') {
this.setMessage({
type: 'success',
context: '确认收货成功'
});
}
else {
console.log(mpShipState);
}
},
fail() {
console.log('fail');
},
complete() {
}
});
}
else {
//引导用户升级微信版本
}
}
}
}
}
});
this.setState({
unsubShip,
});
}
}
},
});