优化提现流程,新增用户确认模式以支持待用户确认的转账,更新相关API和数据库结构以确保数据一致性。同时,调整小程序界面以展示待确认收款信息,提升用户体验。
This commit is contained in:
@@ -158,6 +158,100 @@ export async function createTransfer(params: CreateTransferParams): Promise<Crea
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 用户确认模式:发起转账(需用户在小程序内确认收款)==========
|
||||
// 文档: https://pay.weixin.qq.com/doc/v3/merchant/4012716434
|
||||
|
||||
export interface CreateTransferUserConfirmParams {
|
||||
openid: string
|
||||
amountFen: number
|
||||
outBillNo: string
|
||||
transferRemark?: string
|
||||
}
|
||||
|
||||
export interface CreateTransferUserConfirmResult {
|
||||
success: boolean
|
||||
state?: string
|
||||
packageInfo?: string
|
||||
transferBillNo?: string
|
||||
createTime?: string
|
||||
errorCode?: string
|
||||
errorMessage?: string
|
||||
}
|
||||
|
||||
/** 获取转账结果通知地址 */
|
||||
function getTransferNotifyUrl(): string {
|
||||
const base = process.env.NEXT_PUBLIC_BASE_URL || process.env.VERCEL_URL || 'http://localhost:3000'
|
||||
const host = base.startsWith('http') ? base : `https://${base}`
|
||||
return `${host}/api/payment/wechat/transfer/notify`
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户确认模式 - 发起转账
|
||||
* 返回 WAIT_USER_CONFIRM 时需将 package_info 下发给小程序,用户调 wx.requestMerchantTransfer 确认收款
|
||||
*/
|
||||
export async function createTransferUserConfirm(
|
||||
params: CreateTransferUserConfirmParams
|
||||
): Promise<CreateTransferUserConfirmResult> {
|
||||
const cfg = getConfig()
|
||||
if (!cfg.mchId || !cfg.appId || !cfg.certSerialNo) {
|
||||
return { success: false, errorCode: 'CONFIG_ERROR', errorMessage: '微信转账配置不完整' }
|
||||
}
|
||||
|
||||
const urlPath = '/v3/fund-app/mch-transfer/transfer-bills'
|
||||
const body = {
|
||||
appid: cfg.appId,
|
||||
out_bill_no: params.outBillNo,
|
||||
transfer_scene_id: '1005',
|
||||
openid: params.openid,
|
||||
transfer_amount: params.amountFen,
|
||||
transfer_remark: params.transferRemark || '提现',
|
||||
notify_url: getTransferNotifyUrl(),
|
||||
user_recv_perception: '提现',
|
||||
transfer_scene_report_infos: [
|
||||
{ info_type: '岗位类型', info_content: '兼职人员' },
|
||||
{ info_type: '报酬说明', info_content: '当日兼职费' },
|
||||
],
|
||||
}
|
||||
const bodyStr = JSON.stringify(body)
|
||||
const timestamp = Math.floor(Date.now() / 1000).toString()
|
||||
const nonce = generateNonce()
|
||||
const signature = buildSignature('POST', urlPath, timestamp, nonce, bodyStr)
|
||||
const authorization = buildAuthorization(timestamp, nonce, signature)
|
||||
|
||||
const res = await fetch(`${BASE_URL}${urlPath}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
Authorization: authorization,
|
||||
'User-Agent': 'Soul-Withdraw/1.0',
|
||||
},
|
||||
body: bodyStr,
|
||||
})
|
||||
const data = (await res.json()) as Record<string, unknown>
|
||||
if (res.ok && res.status >= 200 && res.status < 300) {
|
||||
const state = (data.state as string) || ''
|
||||
return {
|
||||
success: true,
|
||||
state,
|
||||
packageInfo: data.package_info as string | undefined,
|
||||
transferBillNo: data.transfer_bill_no as string | undefined,
|
||||
createTime: data.create_time as string | undefined,
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
errorCode: (data.code as string) || 'UNKNOWN',
|
||||
errorMessage: (data.message as string) || (data.error as string) as string || '请求失败',
|
||||
}
|
||||
}
|
||||
|
||||
/** 供小程序调起确认收款时使用:获取商户号与 AppID */
|
||||
export function getTransferMchAndAppId(): { mchId: string; appId: string } {
|
||||
const cfg = getConfig()
|
||||
return { mchId: cfg.mchId, appId: cfg.appId }
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密回调 resource(AEAD_AES_256_GCM)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user