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

486 lines
18 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,
};
}
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,
ship: {
id: 1,
iState: 1,
}
},
order: {
id: 1,
creatorId: 1,
},
entity: 1,
entityId: 1,
creatorId: 1,
phantom3: 1,
};
export default OakComponent({
entity: 'pay',
isList: false,
projection: () => {
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,
depositUnsub: 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);
let showDepositConfirm = false, depositConfirmLoading = false;
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);
showDepositConfirm = true;
depositConfirmLoading = data?.deposit?.ship?.iState !== 'receiving';
}
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,
ship: data?.deposit?.ship,
depositId: data?.depositId,
showDepositConfirm,
depositConfirmLoading,
};
},
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,
});
},
async confirmShipMp() {
//小程序唤起确认收货组件
const { pay, ship, depositId } = this.state;
if (process.env.OAK_PLATFORM === 'wechatMp') {
const shipId = ship.id;
const shipState = ship.iState;
if (process.env.NODE_ENV === 'development') {
if (shipId && shipState === 'receiving') {
await this.features.cache.operate('ship', [{
id: await generateNewIdAsync(),
action: 'succeedReceiving',
data: {},
filter: {
id: shipId,
},
}]);
this.setMessage({
type: 'success',
content: this.t('ship.success')
});
return;
}
}
if (pay.meta.transaction_id) {
if (wx.openBusinessView) {
wx.openBusinessView({
businessType: 'weappOrderConfirm',
extraData: {
transaction_id: pay.meta.transaction_id,
},
success: async () => {
console.log('success');
// const { result: mpShipState } = await this.features.cache.exec('getMpShipState', {
// shipId,
// })
// if (mpShipState === 'received') {
// await this.features.cache.operate('ship',
// [{
// id: await generateNewIdAsync(),
// action: 'succeedReceiving',
// data: {},
// filter: {
// id: shipId,
// },
// }]
// );
// this.reRender();
// this.setMessage({
// type: 'success',
// content: this.t('ship.success')
// });
// } else {
// console.log(mpShipState);
// // this.setMessage({
// // type: 'warning',
// // content: this.t('ship.wait'),
// // })
// this.reRender();
// }
const { result } = await this.features.cache.exec('shipConfirmSuccess', {
shipId,
});
if (result) {
this.setMessage({
type: 'success',
content: this.t('ship.success')
});
}
else {
this.reRender();
}
},
fail: () => {
console.log('fail');
this.setMessage({
type: 'warning',
content: this.t('ship.fail'),
});
},
complete: () => {
}
});
}
else {
//引导用户升级微信版本
this.setMessage({
type: 'warning',
content: this.t('ship.update wechat'),
});
}
}
else {
this.setMessage({
type: 'warning',
content: this.t('no transactionId'),
});
}
}
}
},
lifetimes: {
async ready() {
const { oakId } = this.props;
assert(typeof oakId === 'string');
const unsub = await this.subDataEvents([
`${DATA_SUBSCRIBER_KEYS.payStateChanged}-${oakId}`,
`${DATA_SUBSCRIBER_KEYS.depositStateChanged}-${oakId}`
], async (event, opRecords) => {
for (const record of opRecords) {
console.log(record);
if (record.e === 'pay' || record.e === 'deposit') {
const { f } = record;
const [pay] = this.features.cache.get('pay', {
data: baseProjection,
filter: f,
});
console.log(JSON.stringify(pay));
}
}
});
let depositUnsub = undefined;
const { depositId } = this.state;
if (depositId) {
depositUnsub = await this.subDataEvents([`${DATA_SUBSCRIBER_KEYS.shipStateChanged}-${depositId}`]);
}
this.setState({
unsub,
depositUnsub,
});
},
async mature() {
const { ship } = this.state;
if (ship?.id && ship?.iState === 'receiving') {
await this.confirmShipMp();
}
},
detached() {
const { unsub, depositUnsub } = this.state;
unsub && unsub();
depositUnsub && depositUnsub();
},
},
listeners: {
startPayable(prev, next) {
if (next.startPayable && !this.props.disableAutoPay) {
this.startPay();
}
},
pay(prev, next) {
if (!prev.pay && next.pay) {
this.refreshOfflineAccounts();
}
},
},
});