Files
xinlong-shop-taro/src/api/system/file/index.ts
赵忠林 ca78e73f51 feat(pay): 新增小程序支付页面及相关隐私协议处理
- 新增支付页面组件,实现JSAPI支付流程和多状态页面展示
- 配置支付页面导航条样式和标题
- app.config.ts中添加支付页面路径
- app.tsx中新增微信隐私协议授权弹窗,拦截敏感API授权流程
- api/system/file中导出ensurePrivacyAuthorized用于隐私授权预检
- 登录页和注册页新增useDidShow钩子调用ensurePrivacyAuthorized预检隐私协议
- 登录和注册接口请求迁移至统一request封装,修正响应解构
- 支付流程中新增获取openid和自动登录逻辑,支持未注册用户跳转登录
- 支付成功后通知后端确认并自动跳转到已购产品页面
- 优化支付状态判断逻辑,正确识别支付和订单状态
- 添加错误处理和用户提示,提升支付体验安全性与稳定性
2026-07-16 00:26:42 +08:00

141 lines
4.3 KiB
TypeScript
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 request from '@/utils/request';
import Taro from '@tarojs/taro'
import dayjs from 'dayjs';
import crypto from 'crypto-js';
import {Base64} from 'js-base64';
import {FileRecord} from "@/api/system/file/model";
import {TenantId} from "@/config/app";
export async function uploadOssByPath(filePath: string) {
return new Promise(async (resolve) => {
const date = new Date();
date.setMinutes(date.getMinutes() + 1);
const policyText = {
expiration: date.toISOString(), // 设置policy过期时间。
conditions: [
// 限制上传大小。
["content-length-range", 0, 1024 * 1024 * 1024],
],
};
let sts = Taro.getStorageSync('sts');
const stsExpired = Taro.getStorageSync('stsExpiredAt');
if (!sts || (stsExpired && dayjs().isBefore(dayjs(stsExpired)))) {
// @ts-ignore
const {data: {data: {credentials}}} = await request.get(`https://shop-api.websoft.top/api/oss/getSTSToken`)
Taro.setStorageSync('sts', credentials)
Taro.setStorageSync('stsExpiredAt', credentials.expiration)
sts = credentials
}
const [filename, ext] = filePath.split('.')
const key = `headers/${filename + (Math.random() * 100000)}.${ext}`
const policy = Base64.encode(JSON.stringify(policyText))
const signature = computeSignature(sts.accessKeySecret, policy)
Taro.uploadFile({
url: 'https://oss.wsdns.cn',
name: 'file',
filePath,
formData: {
key,
policy,
OSSAccessKeyId: sts.accessKeyId,
signature,
'x-oss-security-token': sts.securityToken
},
success: () => {
resolve(`https://oss.wsdns.cn/${key}`)
}
})
})
}
const computeSignature = (accessKeySecret: string, canonicalString: string): string => {
return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret));
}
/**
* 在调用涉及用户隐私的 APIchooseImage / chooseMedia / getLocation / getPhoneNumber 等)前,
* 等待用户完成微信隐私协议授权。基础库 3.16.1+ 强制要求,未授权会抛 errno:112。
* 旧基础库或不支持隐私协议的版本直接放行。
*/
export const ensurePrivacyAuthorized = (): Promise<void> => {
return new Promise((resolve) => {
const wxAny: any = Taro
if (typeof wxAny.requirePrivacyAuthorize !== 'function') {
resolve()
return
}
if (typeof wxAny.getPrivacySetting === 'function') {
wxAny.getPrivacySetting({
success: (res: any) => {
if (res && res.needAuthorization) {
wxAny.requirePrivacyAuthorize({
success: () => resolve(),
fail: () => resolve(),
})
} else {
resolve()
}
},
fail: () => resolve(),
})
} else {
wxAny.requirePrivacyAuthorize({
success: () => resolve(),
fail: () => resolve(),
})
}
})
}
/**
* 上传阿里云OSS
*/
export async function uploadFile() {
return new Promise(async (resolve: (result: FileRecord) => void, reject) => {
// 修复:基础库 3.16.1+ 强制隐私协议校验,先确保用户已同意
try {
await ensurePrivacyAuthorized()
} catch {
// 忽略前置授权错误,让 chooseImage 自己抛具体的 fail
}
Taro.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: async (res) => {
const tempFilePath = res.tempFilePaths[0];
// 上传图片到OSS
Taro.uploadFile({
url: 'https://server.websoft.top/api/oss/upload',
filePath: tempFilePath,
name: 'file',
header: {
'content-type': 'application/json',
TenantId
},
success: (res) => {
try {
const data = JSON.parse(res.data);
if (data.code === 0) {
resolve(data.data)
} else {
reject(new Error(data.message || '上传失败'))
}
} catch (error) {
reject(new Error('解析响应数据失败'))
}
},
fail: (err) => {
reject(new Error('上传请求失败'))
}
})
},
fail: (err) => {
reject(new Error('选择图片失败'))
}
});
})
}