fix: 恢复抖音过期支付并完善原生状态确认
This commit is contained in:
@@ -79,3 +79,22 @@ npm run test:douyin 覆盖参数适配、旧调用入口、服务端确认、重
|
|||||||
编译成功,所选 10 项测试通过(含新增 7 项续付测试)。
|
编译成功,所选 10 项测试通过(含新增 7 项续付测试)。
|
||||||
提交目标:后端 hashmall/hashmall 的 dev_douyin_tmp,App 为 charles/frontend-app 的 dev_codex。
|
提交目标:后端 hashmall/hashmall 的 dev_douyin_tmp,App 为 charles/frontend-app 的 dev_codex。
|
||||||
尚未部署或进行真机实付;以上测试均使用模拟渠道,不产生真实交易。
|
尚未部署或进行真机实付;以上测试均使用模拟渠道,不产生真实交易。
|
||||||
|
|
||||||
|
## 过期关单及支付安全修复(2026-09-09 第二次补丁)
|
||||||
|
|
||||||
|
本节覆盖前文旧续付补丁对“过期仍 pending”和“不关单”的行为描述。
|
||||||
|
|
||||||
|
- 后端先查单,过期且 NOTPAY 时请求渠道关单、再次确认,成功后结束本地支付与商品/充值订单。查单失败不放行。
|
||||||
|
- `/payment/douyin/resume` 新增 reason:QUERY_FAILED、CLOSE_UNCONFIRMED、RECONCILIATION_REQUIRED、CHANNEL_PENDING;App 显示相应提示,不暴露后台密钥或完整渠道响应。
|
||||||
|
- closed 解除缓存的旧支付单,用户确认后按当前页面重新请求支付;如果原商品订单已取消,需返回商品页重新下单。
|
||||||
|
- 原生错误码不再被完全隐藏,但 SDK 错误/取消不能单独用于判断渠道订单已关闭。
|
||||||
|
- 插件版本 0.2.0 新增 resetDypay。在服务端确认 paid/closed/unpaid 后重置 UTS/Swift 锁,恢复原生回调丢失场景。迟到回调不作为支付成功依据。
|
||||||
|
- 续付请求超时 60 秒,查单 30 秒;其他请求默认值不变。
|
||||||
|
|
||||||
|
**本次修改了原生插件,必须重新打包 APK/IPA 或自定义基座;不要只对旧基座发布 WGT。**
|
||||||
|
先更新后端 frontend/paycenter/job/mqconsumer 及实际承载退款的服务,再更新 App。
|
||||||
|
无 SQL 迁移。历史已关闭但已收款的异常单保留人工核对,不自动恢复发货或重复付款。
|
||||||
|
前端 `npm run test:douyin` 新增过期旧单放行、取消/切换账号、明确诊断、原生锁恢复等用例;测试不产生真实交易。
|
||||||
|
最终验证:27 项前端支付测试全部通过;后端 41 项选定支付测试通过,包含真实 H2 事务回滚验证。
|
||||||
|
HBuilderX 5.24 App 资源编译及导出成功(有既有 CSS 注释警告)。
|
||||||
|
资源导出通过不代表 Android/iOS 原生构建或真机交易已验证,发布前仍须完成验收。
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const payment = { orderNum: 'order-1', douyin: { appId: 'app', mchId: 'mch', pre
|
|||||||
async function payContext(overrides = {}, app = true) {
|
async function payContext(overrides = {}, app = true) {
|
||||||
const adapter = await import('../utils/douyin-pay-adapter.mjs');
|
const adapter = await import('../utils/douyin-pay-adapter.mjs');
|
||||||
const context = { uni: uniMock(), BASE_URL: 'test-api', TOKEN_NAME: 'token', USER_DATA: 'user',
|
const context = { uni: uniMock(), BASE_URL: 'test-api', TOKEN_NAME: 'token', USER_DATA: 'user',
|
||||||
...adapter, canOpenDypay: () => true, initDypay: () => true,
|
...adapter, canOpenDypay: () => true, initDypay: () => true, resetDypay: () => {},
|
||||||
openDypay: (_, cb) => cb({ resultCode: '0' }),
|
openDypay: (_, cb) => cb({ resultCode: '0' }),
|
||||||
request: async options => options.url === '/payment/douyin/resume'
|
request: async options => options.url === '/payment/douyin/resume'
|
||||||
? ({ bizcode: 100, data: { state: 'paid', orderNum: options.data.orderNum } })
|
? ({ bizcode: 100, data: { state: 'paid', orderNum: options.data.orderNum } })
|
||||||
@@ -214,12 +214,65 @@ test('terminal server result clears saved order without payment and allows the n
|
|||||||
? ({bizcode:100,data:{state,orderNum:payment.orderNum}})
|
? ({bizcode:100,data:{state,orderNum:payment.orderNum}})
|
||||||
: ({bizcode:100,data:{state:2}})});
|
: ({bizcode:100,data:{state:2}})});
|
||||||
ctx.uni.storage.set('tb-douyin:test-api:7', payment.orderNum);
|
ctx.uni.storage.set('tb-douyin:test-api:7', payment.orderNum);
|
||||||
assert.equal(await ctx.prepareDouyinPay('douyin'), false);
|
assert.equal(await ctx.prepareDouyinPay('douyin'), state === 'closed');
|
||||||
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'), false);
|
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'), false);
|
||||||
assert.equal(await ctx.prepareDouyinPay('douyin'), true);
|
assert.equal(await ctx.prepareDouyinPay('douyin'), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('closed old transaction unlocks a fresh server order, never reopens old parameters', async () => {
|
||||||
|
let resets=0, opened;
|
||||||
|
const ctx=await payContext({resetDypay:()=>resets++, request:async options => options.url === '/payment/douyin/resume'
|
||||||
|
? {bizcode:100,data:{state:'closed',orderNum:'expired-order'}} : {bizcode:100,data:{state:2}},
|
||||||
|
openDypay:(options,cb)=>{opened=options.payInfo.prepayid; cb({resultCode:'0'});}});
|
||||||
|
ctx.uni.storage.set('tb-douyin:test-api:7','expired-order');
|
||||||
|
assert.equal(await ctx.prepareDouyinPay('douyin'),true);
|
||||||
|
assert.equal(resets,1);
|
||||||
|
const fresh={...payment,orderNum:'fresh-order',douyin:{...payment.douyin,prepayId:'fresh-prepay'}};
|
||||||
|
await ctx.appDypayFun(fresh);
|
||||||
|
assert.equal(opened,'fresh-prepay');
|
||||||
|
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'),false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('declining new payment or switching accounts after closure never creates a payment', async () => {
|
||||||
|
for(const mode of ['cancel','account']) {
|
||||||
|
const ctx=await payContext({request:async()=>({bizcode:100,data:{state:'closed',orderNum:'expired-order'}})});
|
||||||
|
ctx.uni.storage.set('tb-douyin:test-api:7','expired-order');
|
||||||
|
ctx.uni.showModal=options=>{
|
||||||
|
if(mode==='account') ctx.uni.storage.set('user',{userId:8});
|
||||||
|
options.success({confirm:mode!=='cancel'});
|
||||||
|
};
|
||||||
|
assert.equal(await ctx.prepareDouyinPay('douyin'),false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unconfirmed server state never resets the native lock and explains why', async () => {
|
||||||
|
const ctx=await payContext({resetDypay:()=>assert.fail('Unconfirmed native reset'),
|
||||||
|
request:async()=>({bizcode:100,data:{state:'pending',reason:'QUERY_FAILED',orderNum:payment.orderNum}})});
|
||||||
|
ctx.uni.storage.set('tb-douyin:test-api:7',payment.orderNum);
|
||||||
|
assert.equal(await ctx.prepareDouyinPay('douyin'),false);
|
||||||
|
assert.match(ctx.uni.messages.at(-1),/查单失败/);
|
||||||
|
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unpaid server confirmation resets a missing native callback before reinitializing SDK', async () => {
|
||||||
|
const sequence=[];
|
||||||
|
const ctx=await payContext({resetDypay:()=>sequence.push('reset'),initDypay:()=>{sequence.push('init');return true;},
|
||||||
|
request:async()=>({bizcode:100,data:{state:'unpaid',orderNum:payment.orderNum,payment}}),
|
||||||
|
queryUntilSettled:async()=>({state:'pending'})});
|
||||||
|
ctx.uni.storage.set('tb-douyin:test-api:7',payment.orderNum);
|
||||||
|
assert.equal(await ctx.prepareDouyinPay('douyin'),false);
|
||||||
|
assert.deepEqual(sequence,['reset','init']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SDK rejection stays recoverable but no longer hides the native failure', async () => {
|
||||||
|
const ctx=await payContext({openDypay:(_,cb)=>cb({resultCode:'103'}),queryUntilSettled:async()=>({state:'pending'})});
|
||||||
|
await ctx.appDypayFun(payment);
|
||||||
|
assert.match(ctx.uni.messages.at(-1),/原生支付仍在处理中/);
|
||||||
|
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
|
||||||
|
assert.equal(ctx.uni.messages.includes('支付成功'),false);
|
||||||
|
});
|
||||||
|
|
||||||
test('account switch during resume response or confirmation cannot invoke SDK', async () => {
|
test('account switch during resume response or confirmation cannot invoke SDK', async () => {
|
||||||
for (const phase of ['response','confirmation']) {
|
for (const phase of ['response','confirmation']) {
|
||||||
const ctx = await payContext({openDypay:()=>assert.fail('Old account reached SDK')});
|
const ctx = await payContext({openDypay:()=>assert.fail('Old account reached SDK')});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "tb-douyin-pay",
|
"id": "tb-douyin-pay",
|
||||||
"displayName": "TB 抖音支付",
|
"displayName": "TB 抖音支付",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"description": "独立实现的抖音支付官方 SDK UTS 桥接",
|
"description": "独立实现的抖音支付官方 SDK UTS 桥接",
|
||||||
"engines": { "HBuilderX": ">=4.36.0" },
|
"engines": { "HBuilderX": ">=4.36.0" },
|
||||||
"uni_modules": {
|
"uni_modules": {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { InitOptions, PayOptions, PayCallback } from '../interface.uts'
|
import { InitOptions, PayOptions, PayCallback } from '../interface.uts'
|
||||||
import { configure, invoke } from '../common.uts'
|
import { configure, invoke, resetPending } from '../common.uts'
|
||||||
|
|
||||||
|
export function resetDypay(): void { resetPending() }
|
||||||
|
|
||||||
export function initDypay(options: InitOptions): boolean {
|
export function initDypay(options: InitOptions): boolean {
|
||||||
if (!configure(options.appId)) return false
|
if (!configure(options.appId)) return false
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ public class TbDouyinPayNative {
|
|||||||
|
|
||||||
public static func available() -> Bool { DypayAPI.canOpenDypay() }
|
public static func available() -> Bool { DypayAPI.canOpenDypay() }
|
||||||
|
|
||||||
|
public static func resetPending() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
generation = UUID()
|
||||||
|
pending = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static func pay(_ payload: String, _ done: @escaping (String) -> Void) {
|
public static func pay(_ payload: String, _ done: @escaping (String) -> Void) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
guard pending == nil else {
|
guard pending == nil else {
|
||||||
@@ -34,8 +41,8 @@ public class TbDouyinPayNative {
|
|||||||
DypayAPI.openDypay(withInfo: info, from: controller) { result in
|
DypayAPI.openDypay(withInfo: info, from: controller) { result in
|
||||||
finish(result as? [AnyHashable: Any], ticket)
|
finish(result as? [AnyHashable: Any], ticket)
|
||||||
}
|
}
|
||||||
// Keep a native lock after timeout: a late URL callback cannot identify a new order.
|
// Only authenticated server reconciliation can release a missing callback's lock.
|
||||||
// The host must query the pending order rather than immediately initiate another pay.
|
// URL callbacks are hints, never payment success; the host always queries its saved order.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { UIApplication } from 'UIKit'
|
import { UIApplication } from 'UIKit'
|
||||||
import { URL, NSUserActivity } from 'Foundation'
|
import { URL, NSUserActivity } from 'Foundation'
|
||||||
import { InitOptions, PayOptions, PayCallback } from '../interface.uts'
|
import { InitOptions, PayOptions, PayCallback } from '../interface.uts'
|
||||||
import { configure, invoke } from '../common.uts'
|
import { configure, invoke, resetPending } from '../common.uts'
|
||||||
|
|
||||||
|
export function resetDypay(): void {
|
||||||
|
resetPending()
|
||||||
|
TbDouyinPayNative.resetPending()
|
||||||
|
}
|
||||||
|
|
||||||
export function initDypay(options: InitOptions): boolean {
|
export function initDypay(options: InitOptions): boolean {
|
||||||
if (options.callbackScheme.trim().length == 0 || !configure(options.appId)) return false
|
if (options.callbackScheme.trim().length == 0 || !configure(options.appId)) return false
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ let appId = ''
|
|||||||
let busy = false
|
let busy = false
|
||||||
let sequence = 0
|
let sequence = 0
|
||||||
|
|
||||||
|
// Host calls only after authenticated server reconciliation of the saved transaction.
|
||||||
|
export function resetPending(): void {
|
||||||
|
busy = false
|
||||||
|
sequence += 1
|
||||||
|
}
|
||||||
|
|
||||||
export function configure(id: string): boolean {
|
export function configure(id: string): boolean {
|
||||||
if (busy || id.trim().length == 0) return false
|
if (busy || id.trim().length == 0) return false
|
||||||
appId = id
|
appId = id
|
||||||
|
|||||||
+40
-10
@@ -1,5 +1,5 @@
|
|||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
import { initDypay, canOpenDypay, openDypay } from '@/uni_modules/tb-douyin-pay';
|
import { initDypay, canOpenDypay, openDypay, resetDypay } from '@/uni_modules/tb-douyin-pay';
|
||||||
// #endif
|
// #endif
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import { BASE_URL } from './config.js';
|
import { BASE_URL } from './config.js';
|
||||||
@@ -16,7 +16,7 @@ function accountKey() {
|
|||||||
}
|
}
|
||||||
async function query(orderNum) {
|
async function query(orderNum) {
|
||||||
return queryUntilSettled(async options => {
|
return queryUntilSettled(async options => {
|
||||||
const response = await request({ ...options, isShowLoading: false });
|
const response = await request({ ...options, isShowLoading: false, timeout: 30000 });
|
||||||
if (response?.bizcode !== 100) throw new Error('查询支付结果失败');
|
if (response?.bizcode !== 100) throw new Error('查询支付结果失败');
|
||||||
return response.data;
|
return response.data;
|
||||||
}, orderNum);
|
}, orderNum);
|
||||||
@@ -49,31 +49,40 @@ function showState(state, key, orderNum) {
|
|||||||
if (state.state !== 'pending' && uni.getStorageSync(key) === orderNum) uni.removeStorageSync(key);
|
if (state.state !== 'pending' && uni.getStorageSync(key) === orderNum) uni.removeStorageSync(key);
|
||||||
uni.$emit('douyin-payment-result', { orderNum, state: state.state });
|
uni.$emit('douyin-payment-result', { orderNum, state: state.state });
|
||||||
uni.showToast({ title: state.state === 'paid' ? '支付成功' : state.state === 'failed'
|
uni.showToast({ title: state.state === 'paid' ? '支付成功' : state.state === 'failed'
|
||||||
? '支付未成功,请查看订单' : '支付结果待确认,请稍后查看订单', icon: 'none', duration: 2500 });
|
? '支付未成功,请查看订单' : state.state === 'closed' ? '原支付已关闭,可重新下单'
|
||||||
|
: state.message || '支付结果待确认,请稍后查看订单', icon: 'none', duration: 2500 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called only by a user payment action, never by onShow. Renew the SAME transaction on the server.
|
// Called only by a user payment action, never by onShow. Renew the SAME transaction on the server.
|
||||||
async function continuePendingPayment(key, orderNum) {
|
async function continuePendingPayment(key, orderNum) {
|
||||||
const response = await request({ url: '/payment/douyin/resume', method: 'POST',
|
const response = await request({ url: '/payment/douyin/resume', method: 'POST',
|
||||||
data: { orderNum }, isShowLoading: false });
|
data: { orderNum }, isShowLoading: false, timeout: 60000 });
|
||||||
if (key !== accountKey()) return { state: 'pending' };
|
if (key !== accountKey()) return { state: 'pending' };
|
||||||
if (response?.bizcode !== 100) throw new Error('暂时无法续付,请查看订单后重试');
|
if (response?.bizcode !== 100) throw new Error('暂时无法续付,请查看订单后重试');
|
||||||
const result = response.data;
|
const result = response.data;
|
||||||
if (!result || result.orderNum !== orderNum) throw new Error('续付订单不匹配,请查看订单');
|
if (!result || result.orderNum !== orderNum) throw new Error('续付订单不匹配,请查看订单');
|
||||||
if (result.state === 'paid' || result.state === 'closed') {
|
if (result.state === 'paid' || result.state === 'closed') {
|
||||||
// The existing query endpoint also reconciles business orders when a webhook was missed.
|
// The existing query endpoint also reconciles business orders when a webhook was missed.
|
||||||
const state = result.state === 'paid' ? await query(orderNum) : { state: 'failed' };
|
const state = result.state === 'paid' ? await query(orderNum) : { state: 'closed' };
|
||||||
|
if (key === accountKey() && state.state !== 'pending') resetDypay();
|
||||||
showState(state, key, orderNum);
|
showState(state, key, orderNum);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
if (result.state !== 'unpaid') {
|
if (result.state !== 'unpaid') {
|
||||||
const state = { state: 'pending' };
|
const messages = {
|
||||||
|
QUERY_FAILED: '抖音查单失败,暂不能重新支付,请稍后重试',
|
||||||
|
CLOSE_UNCONFIRMED: '旧支付关单尚未确认,请稍后重试',
|
||||||
|
RECONCILIATION_REQUIRED: '订单状态需核对,请联系客服,勿重复付款',
|
||||||
|
CHANNEL_PENDING: '抖音支付处理中,请稍后查看订单'
|
||||||
|
};
|
||||||
|
const state = { state: 'pending', message: messages[result.reason] };
|
||||||
showState(state, key, orderNum);
|
showState(state, key, orderNum);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
if (result.payment?.orderNum !== orderNum) throw new Error('续付订单不匹配,请查看订单');
|
if (result.payment?.orderNum !== orderNum) throw new Error('续付订单不匹配,请查看订单');
|
||||||
// Validate before the confirmation dialog. Never store the SDK signature locally.
|
// Validate before the confirmation dialog. Never store the SDK signature locally.
|
||||||
toSdkPayInfo(result.payment);
|
toSdkPayInfo(result.payment);
|
||||||
|
resetDypay();
|
||||||
const confirmed = await new Promise(resolve => uni.showModal({
|
const confirmed = await new Promise(resolve => uni.showModal({
|
||||||
title: '继续上一笔支付',
|
title: '继续上一笔支付',
|
||||||
content: `上一笔抖音支付尚未完成,是否继续支付原订单?${result.payment.amount != null
|
content: `上一笔抖音支付尚未完成,是否继续支付原订单?${result.payment.amount != null
|
||||||
@@ -95,8 +104,14 @@ async function executePayment(payment, key) {
|
|||||||
}
|
}
|
||||||
// Persist before entering native code so a killed process remains recoverable.
|
// Persist before entering native code so a killed process remains recoverable.
|
||||||
uni.setStorageSync(key, payment.orderNum);
|
uni.setStorageSync(key, payment.orderNum);
|
||||||
await new Promise(resolve => openDypay({ payInfo, showLoading: true }, resolve));
|
const nativeResult = await new Promise(resolve => openDypay({ payInfo, showLoading: true }, resolve));
|
||||||
const state = await query(payment.orderNum);
|
const state = await query(payment.orderNum);
|
||||||
|
if (state.state === 'pending' && nativeResult?.resultCode && !['0', '3'].includes(String(nativeResult.resultCode))) {
|
||||||
|
// A native error is diagnostic only: never clear a potentially live payment on its authority.
|
||||||
|
const descriptions = { '1': '支付已取消', '2': '支付 SDK 调用失败', '100': '请安装或升级抖音客户端',
|
||||||
|
'103': '原生支付仍在处理中', '-1': '支付参数校验失败' };
|
||||||
|
state.message = `${descriptions[String(nativeResult.resultCode)] || '支付未完成'},请查询订单后重试`;
|
||||||
|
}
|
||||||
showState(state, key, payment.orderNum);
|
showState(state, key, payment.orderNum);
|
||||||
return state;
|
return state;
|
||||||
// #endif
|
// #endif
|
||||||
@@ -116,9 +131,20 @@ export async function prepareDouyinPay(payway) {
|
|||||||
const previous = uni.getStorageSync(key);
|
const previous = uni.getStorageSync(key);
|
||||||
if (previous) {
|
if (previous) {
|
||||||
resuming = true;
|
resuming = true;
|
||||||
try { await continuePendingPayment(key, previous); }
|
try {
|
||||||
|
const result = await continuePendingPayment(key, previous);
|
||||||
|
if (result.state !== 'closed' || key !== accountKey() || uni.getStorageSync(key)) return false;
|
||||||
|
const confirmed = await new Promise(resolve => uni.showModal({
|
||||||
|
title: '原支付已关闭',
|
||||||
|
content: '上一笔支付已确认关闭。是否按当前页面重新发起支付?原商品订单若已过期,请返回商品页重新下单。',
|
||||||
|
confirmText: '重新支付', cancelText: '暂不支付',
|
||||||
|
success: res => resolve(res.confirm === true), fail: () => resolve(false)
|
||||||
|
}));
|
||||||
|
if (!confirmed || key !== accountKey() || uni.getStorageSync(key)) return false;
|
||||||
|
if (!canOpenDypay()) throw new Error('请先安装或升级抖音客户端');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
finally { resuming = false; }
|
finally { resuming = false; }
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
if (!canOpenDypay()) throw new Error('请先安装或升级抖音客户端');
|
if (!canOpenDypay()) throw new Error('请先安装或升级抖音客户端');
|
||||||
return true;
|
return true;
|
||||||
@@ -161,7 +187,11 @@ export async function resumeDouyinPay() {
|
|||||||
const pending = key && uni.getStorageSync(key);
|
const pending = key && uni.getStorageSync(key);
|
||||||
if (!pending) return;
|
if (!pending) return;
|
||||||
resuming = true;
|
resuming = true;
|
||||||
try { showState(await query(pending), key, pending); }
|
try {
|
||||||
|
const state = await query(pending);
|
||||||
|
if (key === accountKey() && state.state !== 'pending') resetDypay();
|
||||||
|
showState(state, key, pending);
|
||||||
|
}
|
||||||
catch (_) { /* Keep the order for a later onShow. Never infer a payment failure. */ }
|
catch (_) { /* Keep the order for a later onShow. Never infer a payment failure. */ }
|
||||||
finally { resuming = false; }
|
finally { resuming = false; }
|
||||||
// #endif
|
// #endif
|
||||||
|
|||||||
+3
-2
@@ -31,7 +31,8 @@ function request(options) {
|
|||||||
method = "GET",
|
method = "GET",
|
||||||
data = {},
|
data = {},
|
||||||
headers = {},
|
headers = {},
|
||||||
isShowLoading = true,
|
isShowLoading = true,
|
||||||
|
timeout = 15000,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const token = getStorageFun(TOKEN_NAME);
|
const token = getStorageFun(TOKEN_NAME);
|
||||||
@@ -60,7 +61,7 @@ function request(options) {
|
|||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...headers,
|
...headers,
|
||||||
},
|
},
|
||||||
timeout: 15000,
|
timeout,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (isShowLoading) {
|
if (isShowLoading) {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
|
|||||||
Reference in New Issue
Block a user