feat:转账插件

This commit is contained in:
2026-07-23 14:31:06 +08:00
parent 4916546322
commit d5a6a16ea5
560 changed files with 71777 additions and 44216 deletions
@@ -0,0 +1,5 @@
{
"dependencies": {
"@tencent/wechat_open_sdk": "1.0.15"
}
}
@@ -0,0 +1,120 @@
import { RequestMerchantTransfer, RequestMerchantTransferGeneralCallbackResult, RequestMerchantTransferOptions } from '../interface.uts'
import {
handleWechatMerchantTransferWant,
requestWechatMerchantTransfer,
WechatMerchantTransferNativeResult
} from './wechatMerchantTransfer.ets'
import {
REQUEST_MERCHANT_TRANSFER_APP_ID_REQUIRED,
REQUEST_MERCHANT_TRANSFER_BUSINESS_FAILED,
REQUEST_MERCHANT_TRANSFER_CANCEL,
REQUEST_MERCHANT_TRANSFER_MCH_ID_REQUIRED,
REQUEST_MERCHANT_TRANSFER_OK,
REQUEST_MERCHANT_TRANSFER_OPEN_SDK_ERROR,
REQUEST_MERCHANT_TRANSFER_PACKAGE_REQUIRED,
REQUEST_MERCHANT_TRANSFER_PARSE_RESPONSE_ERROR,
REQUEST_MERCHANT_TRANSFER_WECHAT_NOT_INSTALLED,
REQUEST_MERCHANT_TRANSFER_WECHAT_VERSION_LOW
} from '../unierror.uts'
let isWechatMerchantTransferHandlerRegistered = false
function ensureWechatMerchantTransferHandlerRegistered() : void {
if (isWechatMerchantTransferHandlerRegistered) {
return
}
isWechatMerchantTransferHandlerRegistered = true
UTSHarmony.onAppAbilityCreate((want, _launchParam) => {
handleWechatMerchantTransferWant(want)
})
UTSHarmony.onAppAbilityNewWant((want, _launchParam) => {
handleWechatMerchantTransferWant(want)
})
}
function invokeCallback(options : RequestMerchantTransferOptions, errMsg : string, isSuccess : boolean) : void {
const result : RequestMerchantTransferGeneralCallbackResult = {
errMsg: errMsg
}
if (isSuccess) {
options.success?.(result)
} else {
options.fail?.(result)
}
options.complete?.(result)
}
function isEmptyString(value : string | null) : boolean {
return value == null || value.length == 0
}
function appendQueryParam(query : string, key : string, value : string) : string {
const param = key + '=' + encodeURIComponent(value)
if (query.length > 0) {
return query + '&' + param
}
return param
}
function getValidateErrorMsg(options : RequestMerchantTransferOptions) : string {
if (isEmptyString(options.mchId)) {
return REQUEST_MERCHANT_TRANSFER_MCH_ID_REQUIRED.errMsg
}
if (isEmptyString(options.package)) {
return REQUEST_MERCHANT_TRANSFER_PACKAGE_REQUIRED.errMsg
}
if (isEmptyString(options.appId)) {
return REQUEST_MERCHANT_TRANSFER_APP_ID_REQUIRED.errMsg
}
return ''
}
function buildRequestMerchantTransferQuery(options : RequestMerchantTransferOptions, appId : string) : string {
let query = ''
// 微信官方文档上只有下方3个参数,没有 openId、subAppId、subMchId
// 相关文档:
// 用户确认收款:https://pay.weixin.qq.com/doc/v3/merchant/4019670774
// 免确认收款授权:https://pay.weixin.qq.com/doc/v3/merchant/4020320137
query = appendQueryParam(query, 'mchId', options.mchId)
query = appendQueryParam(query, 'package', options.package)
query = appendQueryParam(query, 'appId', appId)
return query
}
function handleNativeResult(options : RequestMerchantTransferOptions, nativeResult : WechatMerchantTransferNativeResult) : void {
if (nativeResult.result == 'success') {
invokeCallback(options, REQUEST_MERCHANT_TRANSFER_OK, true)
return
}
let errMsg = REQUEST_MERCHANT_TRANSFER_BUSINESS_FAILED.errMsg
if (nativeResult.result == 'cancel') {
errMsg = REQUEST_MERCHANT_TRANSFER_CANCEL.errMsg
} else if (nativeResult.result == 'wechat_not_installed') {
errMsg = REQUEST_MERCHANT_TRANSFER_WECHAT_NOT_INSTALLED.errMsg
} else if (nativeResult.result == 'wechat_version_low') {
errMsg = REQUEST_MERCHANT_TRANSFER_WECHAT_VERSION_LOW.errMsg
} else if (nativeResult.result == 'parse_response_error') {
errMsg = REQUEST_MERCHANT_TRANSFER_PARSE_RESPONSE_ERROR.errMsg
} else if (nativeResult.result == 'open_sdk_error' || nativeResult.result == 'timeout' || nativeResult.result == 'request_interrupted') {
errMsg = REQUEST_MERCHANT_TRANSFER_OPEN_SDK_ERROR.errMsg
}
invokeCallback(options, errMsg, false)
}
export const requestMerchantTransfer : RequestMerchantTransfer = function (options : RequestMerchantTransferOptions) {
const validateErrorMsg = getValidateErrorMsg(options)
if (validateErrorMsg.length > 0) {
invokeCallback(options, validateErrorMsg, false)
return
}
ensureWechatMerchantTransferHandlerRegistered()
const appId : string = options.appId ?? ''
const query = buildRequestMerchantTransferQuery(options, appId)
requestWechatMerchantTransfer(appId, query).then((nativeResult : WechatMerchantTransferNativeResult) => {
handleNativeResult(options, nativeResult)
}).catch((_error : Error) => {
invokeCallback(options, REQUEST_MERCHANT_TRANSFER_OPEN_SDK_ERROR.errMsg, false)
})
}
@@ -0,0 +1,161 @@
import { common, Want } from '@kit.AbilityKit';
import * as wxopensdk from '@tencent/wechat_open_sdk';
export class WechatMerchantTransferNativeResult {
result: string = '';
errMsg: string = '';
extMsg: string = '';
constructor(result: string, errMsg: string, extMsg: string) {
this.result = result;
this.errMsg = errMsg;
this.extMsg = extMsg;
}
}
const MERCHANT_TRANSFER_BUSINESS_TYPE: string = 'requestMerchantTransfer';
const REQUEST_TIMEOUT_MS: number = 120000;
let currentAppId: string = '';
let currentWxApi: wxopensdk.WXApi | null = null;
let pendingResolve: ((result: WechatMerchantTransferNativeResult) => void) | null = null;
let timeoutId: number = -1;
class MerchantTransferRespPayload {
result: string | null = null;
}
function buildResult(result: string, errMsg: string, extMsg: string): WechatMerchantTransferNativeResult {
return new WechatMerchantTransferNativeResult(result, errMsg, extMsg);
}
function getWxApi(appId: string): wxopensdk.WXApi {
let wxApi = currentWxApi;
if (wxApi == null || currentAppId != appId) {
currentAppId = appId;
wxApi = wxopensdk.WXAPIFactory.createWXAPI(appId);
currentWxApi = wxApi;
}
return wxApi;
}
function clearPendingTimeout(): void {
if (timeoutId != -1) {
clearTimeout(timeoutId);
timeoutId = -1;
}
}
function finishPending(result: WechatMerchantTransferNativeResult): void {
const resolve = pendingResolve;
pendingResolve = null;
clearPendingTimeout();
if (resolve != null) {
resolve(result);
}
}
function parseMerchantTransferResp(extMsg: string): WechatMerchantTransferNativeResult {
if (extMsg.length == 0) {
return buildResult('parse_response_error', 'parse response failed', extMsg);
}
try {
const payload = JSON.parse(extMsg) as MerchantTransferRespPayload;
const result: string = payload.result ?? '';
if (result == 'success') {
return buildResult('success', '', extMsg);
}
if (result == 'fail') {
return buildResult('fail', 'business failed', extMsg);
}
if (result == 'cancel') {
return buildResult('cancel', 'business canceled', extMsg);
}
return buildResult('business_failed', 'unknown business result', extMsg);
} catch (_error) {
return buildResult('parse_response_error', 'parse response failed', extMsg);
}
}
class WechatMerchantTransferEventHandler implements wxopensdk.WXApiEventHandler {
onResp(resp: wxopensdk.BaseResp): void {
if (resp instanceof wxopensdk.OpenBusinessViewResp) {
if ("requestMerchantTransfer" === resp.businessType) {
if (resp.errCode == wxopensdk.ErrCode.ERR_UNSUPPORTED) {
finishPending(buildResult('wechat_version_low', resp.errStr ?? 'wechat version is too low', ''));
return;
}
const businessType: string = resp.businessType ?? '';
if (businessType != MERCHANT_TRANSFER_BUSINESS_TYPE) {
return;
}
const extMsg: string = resp.extMsg ?? '';
if (resp.errCode == wxopensdk.ErrCode.ERR_USER_CANCEL && extMsg.length == 0) {
finishPending(buildResult('cancel', resp.errStr ?? 'business canceled', ''));
return;
}
finishPending(parseMerchantTransferResp(extMsg));
}
}
}
onReq(_req: wxopensdk.BaseReq): void {
}
}
const wxEventHandler = new WechatMerchantTransferEventHandler();
export async function requestWechatMerchantTransfer(appId: string, query: string): Promise<WechatMerchantTransferNativeResult> {
if (appId == '') {
return buildResult('invalid_app_id', 'appId is empty', '');
}
if (query == '') {
return buildResult('invalid_query', 'query is empty', '');
}
const wxApi: wxopensdk.WXApi = getWxApi(appId);
if (!wxApi.isWXAppInstalled()) {
return buildResult('wechat_not_installed', 'wechat is not installed', '');
}
const req = new wxopensdk.OpenBusinessViewReq();
req.businessType = MERCHANT_TRANSFER_BUSINESS_TYPE;
req.query = query;
const context = getContext() as common.UIAbilityContext;
const responsePromise = new Promise<WechatMerchantTransferNativeResult>((resolve) => {
if (pendingResolve != null) {
finishPending(buildResult('request_interrupted', 'another merchant transfer request started', ''));
}
pendingResolve = resolve;
timeoutId = setTimeout(() => {
finishPending(buildResult('timeout', 'wait wechat response timeout', ''));
}, REQUEST_TIMEOUT_MS);
});
try {
const sendResult: boolean = await wxApi.sendReq(context, req);
if (!sendResult) {
finishPending(buildResult('open_sdk_error', 'sendReq returned false', ''));
}
} catch (_error) {
finishPending(buildResult('open_sdk_error', 'sendReq failed', ''));
}
return responsePromise;
}
export function handleWechatMerchantTransferWant(want: Want): void {
const wxApi = currentWxApi;
if (wxApi == null || currentAppId.length == 0) {
return;
}
wxApi.handleWant(want, wxEventHandler);
}