支付宝兼容

This commit is contained in:
2026-03-09 09:07:58 +08:00
parent 069677957e
commit b3836b8bf2
31 changed files with 2382 additions and 307 deletions
+386 -6
View File
@@ -36,6 +36,206 @@ const getPermissionText = (key) => {
return texts[key] || permissionTexts[DEFAULT_LOCALE][key] || ''
}
// 兼容多端:部分平台(如支付宝小程序)经纬度可能返回字符串
// 统一先转为 Number 再做 toFixed,避免 "toFixed is not a function"
const toFixedNumber = (value, digits = 5) => {
const n = Number(value)
if (!Number.isFinite(n)) return null
return Number(n.toFixed(digits))
}
// =============================
// 支付宝小程序:高德地图 WebService(需要 key + 安全密钥签名)
// 说明:支付宝小程序下仅使用高德 key(你提供的 key/secret),避免腾讯地图 key 不可用的问题
// =============================
// #ifdef MP-ALIPAY
const AMAP_KEY = '1c6df40606891377b33576e7876af6ac'
const AMAP_SECRET = '00ea790d0b24190174c598199b183750'
const AMAP_BASE_URL = 'https://restapi.amap.com/v3/'
// 轻量 MD5RFC1321)实现:用于高德 WebService 的 sig 参数
// 仅在 MP-ALIPAY 编译进包,避免影响其它端体积
function md5(str) {
/* eslint-disable */
function cmn(q, a, b, x, s, t) {
a = add32(add32(a, q), add32(x, t));
return add32((a << s) | (a >>> (32 - s)), b);
}
function ff(a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); }
function gg(a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); }
function hh(a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); }
function ii(a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); }
function md5cycle(x, k) {
let a = x[0], b = x[1], c = x[2], d = x[3];
a = ff(a, b, c, d, k[0], 7, -680876936);
d = ff(d, a, b, c, k[1], 12, -389564586);
c = ff(c, d, a, b, k[2], 17, 606105819);
b = ff(b, c, d, a, k[3], 22, -1044525330);
a = ff(a, b, c, d, k[4], 7, -176418897);
d = ff(d, a, b, c, k[5], 12, 1200080426);
c = ff(c, d, a, b, k[6], 17, -1473231341);
b = ff(b, c, d, a, k[7], 22, -45705983);
a = ff(a, b, c, d, k[8], 7, 1770035416);
d = ff(d, a, b, c, k[9], 12, -1958414417);
c = ff(c, d, a, b, k[10], 17, -42063);
b = ff(b, c, d, a, k[11], 22, -1990404162);
a = ff(a, b, c, d, k[12], 7, 1804603682);
d = ff(d, a, b, c, k[13], 12, -40341101);
c = ff(c, d, a, b, k[14], 17, -1502002290);
b = ff(b, c, d, a, k[15], 22, 1236535329);
a = gg(a, b, c, d, k[1], 5, -165796510);
d = gg(d, a, b, c, k[6], 9, -1069501632);
c = gg(c, d, a, b, k[11], 14, 643717713);
b = gg(b, c, d, a, k[0], 20, -373897302);
a = gg(a, b, c, d, k[5], 5, -701558691);
d = gg(d, a, b, c, k[10], 9, 38016083);
c = gg(c, d, a, b, k[15], 14, -660478335);
b = gg(b, c, d, a, k[4], 20, -405537848);
a = gg(a, b, c, d, k[9], 5, 568446438);
d = gg(d, a, b, c, k[14], 9, -1019803690);
c = gg(c, d, a, b, k[3], 14, -187363961);
b = gg(b, c, d, a, k[8], 20, 1163531501);
a = gg(a, b, c, d, k[13], 5, -1444681467);
d = gg(d, a, b, c, k[2], 9, -51403784);
c = gg(c, d, a, b, k[7], 14, 1735328473);
b = gg(b, c, d, a, k[12], 20, -1926607734);
a = hh(a, b, c, d, k[5], 4, -378558);
d = hh(d, a, b, c, k[8], 11, -2022574463);
c = hh(c, d, a, b, k[11], 16, 1839030562);
b = hh(b, c, d, a, k[14], 23, -35309556);
a = hh(a, b, c, d, k[1], 4, -1530992060);
d = hh(d, a, b, c, k[4], 11, 1272893353);
c = hh(c, d, a, b, k[7], 16, -155497632);
b = hh(b, c, d, a, k[10], 23, -1094730640);
a = hh(a, b, c, d, k[13], 4, 681279174);
d = hh(d, a, b, c, k[0], 11, -358537222);
c = hh(c, d, a, b, k[3], 16, -722521979);
b = hh(b, c, d, a, k[6], 23, 76029189);
a = hh(a, b, c, d, k[9], 4, -640364487);
d = hh(d, a, b, c, k[12], 11, -421815835);
c = hh(c, d, a, b, k[15], 16, 530742520);
b = hh(b, c, d, a, k[2], 23, -995338651);
a = ii(a, b, c, d, k[0], 6, -198630844);
d = ii(d, a, b, c, k[7], 10, 1126891415);
c = ii(c, d, a, b, k[14], 15, -1416354905);
b = ii(b, c, d, a, k[5], 21, -57434055);
a = ii(a, b, c, d, k[12], 6, 1700485571);
d = ii(d, a, b, c, k[3], 10, -1894986606);
c = ii(c, d, a, b, k[10], 15, -1051523);
b = ii(b, c, d, a, k[1], 21, -2054922799);
a = ii(a, b, c, d, k[8], 6, 1873313359);
d = ii(d, a, b, c, k[15], 10, -30611744);
c = ii(c, d, a, b, k[6], 15, -1560198380);
b = ii(b, c, d, a, k[13], 21, 1309151649);
a = ii(a, b, c, d, k[4], 6, -145523070);
d = ii(d, a, b, c, k[11], 10, -1120210379);
c = ii(c, d, a, b, k[2], 15, 718787259);
b = ii(b, c, d, a, k[9], 21, -343485551);
x[0] = add32(a, x[0]);
x[1] = add32(b, x[1]);
x[2] = add32(c, x[2]);
x[3] = add32(d, x[3]);
}
function md5blk(s) {
const md5blks = [];
for (let i = 0; i < 64; i += 4) {
md5blks[i >> 2] = s.charCodeAt(i) +
(s.charCodeAt(i + 1) << 8) +
(s.charCodeAt(i + 2) << 16) +
(s.charCodeAt(i + 3) << 24);
}
return md5blks;
}
function md5blk_array(a) {
const md5blks = [];
for (let i = 0; i < 64; i += 4) {
md5blks[i >> 2] = a[i] +
(a[i + 1] << 8) +
(a[i + 2] << 16) +
(a[i + 3] << 24);
}
return md5blks;
}
function md51(s) {
let n = s.length;
let state = [1732584193, -271733879, -1732584194, 271733878];
let i;
for (i = 64; i <= n; i += 64) {
md5cycle(state, md5blk(s.substring(i - 64, i)));
}
s = s.substring(i - 64);
const tail = new Array(64).fill(0);
for (i = 0; i < s.length; i++) tail[i] = s.charCodeAt(i);
tail[i] = 0x80;
if (i > 55) {
md5cycle(state, md5blk_array(tail));
for (i = 0; i < 64; i++) tail[i] = 0;
}
const tmp = n * 8;
tail[56] = tmp & 0xFF;
tail[57] = (tmp >>> 8) & 0xFF;
tail[58] = (tmp >>> 16) & 0xFF;
tail[59] = (tmp >>> 24) & 0xFF;
md5cycle(state, md5blk_array(tail));
return state;
}
function rhex(n) {
const s = '0123456789abcdef';
let j, out = '';
for (j = 0; j < 4; j++) {
out += s.charAt((n >> (j * 8 + 4)) & 0x0F) + s.charAt((n >> (j * 8)) & 0x0F);
}
return out;
}
function hex(x) { return x.map(rhex).join(''); }
function add32(a, b) { return (a + b) & 0xFFFFFFFF; }
return hex(md51(str));
/* eslint-enable */
}
function buildAmapQuery(params) {
const clean = {}
Object.keys(params || {}).forEach((k) => {
const v = params[k]
if (v === undefined || v === null || v === '') return
clean[k] = String(v)
})
// 必须包含 key
if (!clean.key) clean.key = AMAP_KEY
const keys = Object.keys(clean).sort()
const query = keys.map((k) => `${k}=${encodeURIComponent(clean[k])}`).join('&')
const sig = md5(query + AMAP_SECRET)
return `${query}&sig=${sig}`
}
function amapGet(path, params) {
return new Promise((resolve, reject) => {
const query = buildAmapQuery(params)
uni.request({
url: `${AMAP_BASE_URL}${path}?${query}`,
method: 'GET',
header: { 'content-type': 'application/json' },
success: (res) => {
const data = res && res.data
// 高德 WebServicestatus '1' 表示成功
if (data && String(data.status) === '1') {
resolve(data)
} else {
reject(data || { status: '0', info: '请求失败' })
}
},
fail: (err) => reject(err)
})
})
}
// #endif
// 腾讯地图Key
const QQMAP_KEY =
// #ifdef H5
@@ -566,8 +766,15 @@ function getUserLocation() {
wx.getLocation({
type: 'gcj02',
success: (res) => {
const longitude = parseFloat(res.longitude.toFixed(5));
const latitude = parseFloat(res.latitude.toFixed(5));
const longitude = toFixedNumber(res.longitude, 5)
const latitude = toFixedNumber(res.latitude, 5)
if (longitude === null || latitude === null) {
reject({
code: 'INVALID_COORD',
errMsg: 'invalid longitude/latitude from getLocation'
})
return
}
console.log('地址获取成功');
resolve({
longitude,
@@ -612,8 +819,15 @@ function getUserLocation() {
wx.getLocation({
type: 'gcj02',
success: (res) => {
const longitude = parseFloat(res.longitude.toFixed(5));
const latitude = parseFloat(res.latitude.toFixed(5));
const longitude = toFixedNumber(res.longitude, 5)
const latitude = toFixedNumber(res.latitude, 5)
if (longitude === null || latitude === null) {
reject({
code: 'INVALID_COORD',
errMsg: 'invalid longitude/latitude from getLocation'
})
return
}
console.log('地址获取成功');
resolve({
longitude,
@@ -635,8 +849,15 @@ function getUserLocation() {
uni.getLocation({
type: 'gcj02',
success: (res) => {
const longitude = parseFloat(res.longitude.toFixed(5));
const latitude = parseFloat(res.latitude.toFixed(5));
const longitude = toFixedNumber(res.longitude, 5)
const latitude = toFixedNumber(res.latitude, 5)
if (longitude === null || latitude === null) {
reject({
code: 'INVALID_COORD',
errMsg: 'invalid longitude/latitude from getLocation'
})
return
}
console.log('地址获取成功');
resolve({
longitude,
@@ -665,6 +886,41 @@ function getUserLocation() {
// 逆地理编码 - 根据经纬度获取地址信息
function getRegeo(longitude, latitude) {
return new Promise((resolve, reject) => {
// #ifdef MP-ALIPAY
// 支付宝小程序:高德逆地理编码
const lng = toFixedNumber(longitude, 6)
const lat = toFixedNumber(latitude, 6)
if (lng === null || lat === null) {
reject({ success: false, message: '无效经纬度' })
return
}
amapGet('geocode/regeo', {
location: `${lng},${lat}`,
radius: 1000,
extensions: 'base'
}).then((data) => {
const regeocode = data.regeocode || {}
const ac = regeocode.addressComponent || {}
resolve({
success: true,
data: {
formatted_address: regeocode.formatted_address || '',
addressComponent: {
city: Array.isArray(ac.city) ? '' : (ac.city || ''),
district: ac.district || '',
province: ac.province || '',
street: (ac.streetNumber && ac.streetNumber.street) || '',
street_number: (ac.streetNumber && ac.streetNumber.number) || ''
}
}
})
}).catch((err) => {
console.error('支付宝-高德逆地理编码失败:', err)
reject({ success: false, message: err.info || err.message || '逆地理编码失败' })
})
return
// #endif
// #ifdef H5
// H5环境:使用JSONP方式调用腾讯地图API,避免跨域问题
const callbackName = `qqmap_geocoder_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
@@ -756,6 +1012,50 @@ function getRegeo(longitude, latitude) {
// 搜索周边POI
function getPoiAround(longitude, latitude, keyword = '', radius = 1000) {
return new Promise((resolve, reject) => {
// #ifdef MP-ALIPAY
// 支付宝小程序:高德周边检索
const lng = toFixedNumber(longitude, 6)
const lat = toFixedNumber(latitude, 6)
if (lng === null || lat === null) {
reject({ success: false, message: '无效经纬度' })
return
}
amapGet('place/around', {
location: `${lng},${lat}`,
keywords: keyword || '',
radius: radius || 1000,
sortrule: 'distance',
offset: 10,
page: 1,
extensions: 'base'
}).then((data) => {
const pois = Array.isArray(data.pois) ? data.pois : []
const list = pois.map((p) => {
const loc = (p.location || '').split(',')
const plng = toFixedNumber(loc[0], 6)
const plat = toFixedNumber(loc[1], 6)
return {
id: p.id || null,
title: p.name || null,
latitude: plat,
longitude: plng,
address: p.address || null,
category: p.type || null,
tel: p.tel || null,
adcode: p.adcode || null,
city: p.cityname || null,
district: p.adname || null,
province: p.pname || null
}
})
resolve({ success: true, data: list })
}).catch((err) => {
console.error('支付宝-高德搜索POI失败:', err)
reject({ success: false, message: err.info || err.message || '搜索POI失败' })
})
return
// #endif
// #ifdef H5
// H5环境:使用JSONP方式调用腾讯地图API
const callbackName = `qqmap_search_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
@@ -843,6 +1143,48 @@ function getPoiAround(longitude, latitude, keyword = '', radius = 1000) {
// 计算距离(异步)
function calculateDistance(from, to) {
return new Promise((resolve, reject) => {
// #ifdef MP-ALIPAY
// 支付宝小程序:高德距离计算(支持多个目的地)
if (!from || !to) {
reject({ success: false, message: '参数缺失' })
return
}
const fromLng = toFixedNumber((from && (from.longitude !== undefined ? from.longitude : from.lng)), 6)
const fromLat = toFixedNumber((from && (from.latitude !== undefined ? from.latitude : from.lat)), 6)
if (fromLng === null || fromLat === null) {
reject({ success: false, message: '无效起点坐标' })
return
}
const toArr = Array.isArray(to) ? to : [to]
const origins = toArr.map((p) => {
const lng = toFixedNumber((p && (p.longitude !== undefined ? p.longitude : p.lng)), 6)
const lat = toFixedNumber((p && (p.latitude !== undefined ? p.latitude : p.lat)), 6)
return (lng === null || lat === null) ? null : `${lng},${lat}`
}).filter(Boolean)
if (!origins.length) {
reject({ success: false, message: '无效终点坐标' })
return
}
// 高德接口:origins(多个) + destination(单个)
amapGet('distance', {
origins: origins.join('|'),
destination: `${fromLng},${fromLat}`,
type: 0 // 0:驾车距离;步行/骑行需要其它接口,这里保持与原来“直线/近似”用途一致
}).then((data) => {
const results = Array.isArray(data.results) ? data.results : []
const distances = results.map((r) => Number(r.distance)).filter((n) => Number.isFinite(n))
// 保持与原实现一致:始终返回数组(即使只传了一个目的地)
resolve({ success: true, data: distances })
}).catch((err) => {
console.error('支付宝-高德计算距离失败:', err)
reject({ success: false, message: err.info || err.message || '计算距离失败' })
})
return
// #endif
// #ifdef H5
// H5环境:使用JSONP方式调用腾讯地图API
const callbackName = `qqmap_distance_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
@@ -935,6 +1277,44 @@ function calculateDistanceSync(lat1, lng1, lat2, lng2) {
// 关键词提示
function getSuggestion(keyword, region = '全国') {
return new Promise((resolve, reject) => {
// #ifdef MP-ALIPAY
// 支付宝小程序:高德输入提示
if (!keyword) {
resolve({ success: true, data: [] })
return
}
amapGet('assistant/inputtips', {
keywords: keyword,
city: region && region !== '全国' ? region : '',
citylimit: region && region !== '全国' ? 1 : 0
}).then((data) => {
const tips = Array.isArray(data.tips) ? data.tips : []
const list = tips.map((t) => {
const loc = (t.location || '').split(',')
const lng = toFixedNumber(loc[0], 6)
const lat = toFixedNumber(loc[1], 6)
return {
adcode: t.adcode || null,
address: t.address || null,
category: t.type || null,
city: t.cityname || null,
district: t.district || null,
id: t.id || null,
latitude: lat,
longitude: lng,
province: t.pname || null,
title: t.name || null,
type: t.type || null
}
})
resolve({ success: true, data: list })
}).catch((err) => {
console.error('支付宝-高德关键词提示失败:', err)
reject({ success: false, message: err.info || err.message || '关键词提示失败' })
})
return
// #endif
// #ifdef H5
// H5环境:使用JSONP方式调用腾讯地图API
const callbackName = `qqmap_suggestion_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;