|
@@ -1,7 +1,159 @@
|
|
|
-import { createSign } from 'crypto'
|
|
|
|
|
-function requestServer(header: any, body: any, url: string) {
|
|
|
|
|
- const req = {
|
|
|
|
|
|
|
+import { readFileSync } from 'fs'
|
|
|
|
|
+import * as crypto from 'crypto'
|
|
|
|
|
+import axios from 'axios'
|
|
|
|
|
+import { format, addSeconds } from 'date-fns'
|
|
|
|
|
+import * as qs from 'querystring'
|
|
|
|
|
+import * as path from 'path'
|
|
|
|
|
+import { InternalServerErrorException, Logger } from '@nestjs/common'
|
|
|
|
|
+
|
|
|
|
|
+const TAG = 'SandPay'
|
|
|
|
|
+
|
|
|
|
|
+const privateKey = crypto.createPrivateKey({
|
|
|
|
|
+ key: readFileSync(path.join(__dirname, '..', '..', 'certs', '6888806043057.key'))
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const publicKey = crypto.createPublicKey({
|
|
|
|
|
+ key: readFileSync(path.join(__dirname, '..', '..', 'certs', 'sand.crt'))
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+function privSign(str) {
|
|
|
|
|
+ const signer = crypto.createSign('SHA1')
|
|
|
|
|
+ signer.update(str)
|
|
|
|
|
+ signer.end()
|
|
|
|
|
+ const signature = signer.sign(privateKey)
|
|
|
|
|
+ return signature.toString('base64')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function verifySign(str, sign) {
|
|
|
|
|
+ const verifier = crypto.createVerify('SHA1')
|
|
|
|
|
+ verifier.update(str)
|
|
|
|
|
+ verifier.end()
|
|
|
|
|
+ return verifier.verify(publicKey, Buffer.from(sign.replace(/\s/g, '+'), 'base64'))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function request(header, body, url) {
|
|
|
|
|
+ const data = {
|
|
|
head: header,
|
|
head: header,
|
|
|
- body: body
|
|
|
|
|
|
|
+ body
|
|
|
|
|
+ }
|
|
|
|
|
+ const dataStr = JSON.stringify(data)
|
|
|
|
|
+ console.log(JSON.stringify(data, null, 4))
|
|
|
|
|
+ const sign = await privSign(dataStr)
|
|
|
|
|
+ const postBody = {
|
|
|
|
|
+ charset: 'UTF-8',
|
|
|
|
|
+ data: dataStr,
|
|
|
|
|
+ signType: '01',
|
|
|
|
|
+ sign,
|
|
|
|
|
+ extend: ''
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log(qs.stringify(postBody))
|
|
|
|
|
+ return await axios.post(url, qs.stringify(postBody), {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
|
|
|
|
|
+ },
|
|
|
|
|
+ timeout: 30000,
|
|
|
|
|
+ timeoutErrorMessage: '请求超时'
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+const algorithm = 'aes-128-ecb'
|
|
|
|
|
+async function withdraw(name, bank, amount) {
|
|
|
|
|
+ const data = {
|
|
|
|
|
+ cityNo: '',
|
|
|
|
|
+ productId: '00000004',
|
|
|
|
|
+ bankType: '',
|
|
|
|
|
+ payMode: '',
|
|
|
|
|
+ accNo: bank,
|
|
|
|
|
+ accName: name,
|
|
|
|
|
+ bankName: '',
|
|
|
|
|
+ remark: '消费',
|
|
|
|
|
+ channelType: '',
|
|
|
|
|
+ accAttr: '0',
|
|
|
|
|
+ version: '01',
|
|
|
|
|
+ timeOut: format(addSeconds(new Date(), 180), 'yyyyMMddHHmmss'),
|
|
|
|
|
+ extend: '',
|
|
|
|
|
+ extendParams: '',
|
|
|
|
|
+ tranTime: format(new Date(), 'yyyyMMddHHmmss'),
|
|
|
|
|
+ provNo: '',
|
|
|
|
|
+ phone: '',
|
|
|
|
|
+ tranAmt: (amount * 100).toFixed(0).padStart(12, '0'),
|
|
|
|
|
+ reqReserved: '',
|
|
|
|
|
+ orderCode: new Date().getTime() + '',
|
|
|
|
|
+ accType: '4',
|
|
|
|
|
+ currencyCode: '156'
|
|
|
|
|
+ }
|
|
|
|
|
+ const dataStr = JSON.stringify(data)
|
|
|
|
|
+
|
|
|
|
|
+ // 加密密钥,必须是 16、24 或 32 个字符长(分别对应 AES-128、AES-192 或 AES-256)
|
|
|
|
|
+ const key = crypto.randomBytes(16)
|
|
|
|
|
+
|
|
|
|
|
+ // 加密算法使用 AES-256-ECB
|
|
|
|
|
+
|
|
|
|
|
+ // 将要加密的数据转换为一个 Buffer 对象
|
|
|
|
|
+ const dataBuffer = Buffer.from(dataStr, 'utf8')
|
|
|
|
|
+
|
|
|
|
|
+ // 创建一个加密器对象,使用 PKCS5Padding 进行补位
|
|
|
|
|
+ const cipher = crypto.createCipheriv(algorithm, key, null)
|
|
|
|
|
+ cipher.setAutoPadding(true)
|
|
|
|
|
+
|
|
|
|
|
+ // 加密数据,并返回加密后的 Buffer 对象
|
|
|
|
|
+ const encryptedBuffer = Buffer.concat([cipher.update(dataBuffer), cipher.final()])
|
|
|
|
|
+
|
|
|
|
|
+ // 将加密后的 Buffer 对象转换为十六进制字符串
|
|
|
|
|
+ const encrypted = encryptedBuffer.toString('base64')
|
|
|
|
|
+
|
|
|
|
|
+ const sign = await privSign(dataStr)
|
|
|
|
|
+
|
|
|
|
|
+ const encryptKey = crypto
|
|
|
|
|
+ .publicEncrypt(
|
|
|
|
|
+ {
|
|
|
|
|
+ key: publicKey,
|
|
|
|
|
+ padding: crypto.constants.RSA_PKCS1_PADDING
|
|
|
|
|
+ },
|
|
|
|
|
+ key
|
|
|
|
|
+ )
|
|
|
|
|
+ .toString('base64')
|
|
|
|
|
+
|
|
|
|
|
+ const body = {
|
|
|
|
|
+ transCode: 'RTPM',
|
|
|
|
|
+ accessType: '0',
|
|
|
|
|
+ merId: '6888806043057',
|
|
|
|
|
+ encryptKey: encryptKey,
|
|
|
|
|
+ encryptData: encrypted,
|
|
|
|
|
+ sign,
|
|
|
|
|
+ extend: ''
|
|
|
|
|
+ }
|
|
|
|
|
+ const res = await axios.post('http://120.78.171.194:11223/agent-main/openapi/agentpay', qs.stringify(body))
|
|
|
|
|
+
|
|
|
|
|
+ const retData = qs.parse(qs.unescape(res.data))
|
|
|
|
|
+
|
|
|
|
|
+ const decryptKey = crypto.privateDecrypt(
|
|
|
|
|
+ {
|
|
|
|
|
+ key: privateKey,
|
|
|
|
|
+ padding: crypto.constants.RSA_PKCS1_PADDING
|
|
|
|
|
+ },
|
|
|
|
|
+ Buffer.from((retData.encryptKey as string).replace(/\s/g, '+'), 'base64')
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ const decipher = crypto.createDecipheriv(algorithm, decryptKey, null)
|
|
|
|
|
+ decipher.setAutoPadding(true)
|
|
|
|
|
+
|
|
|
|
|
+ const decryptedBuffer = Buffer.concat([
|
|
|
|
|
+ decipher.update(Buffer.from((retData.encryptData as string).replace(/\s/g, '+'), 'base64')),
|
|
|
|
|
+ decipher.final()
|
|
|
|
|
+ ])
|
|
|
|
|
+ const decrypted = decryptedBuffer.toString('utf8')
|
|
|
|
|
+
|
|
|
|
|
+ const verify = verifySign(decrypted, retData.sign)
|
|
|
|
|
+
|
|
|
|
|
+ if (!verify) {
|
|
|
|
|
+ throw new InternalServerErrorException('验签失败')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const ret = JSON.parse(decrypted)
|
|
|
|
|
+
|
|
|
|
|
+ Logger.log(`withdraw resp=${JSON.stringify(ret, null, 4)}`, TAG)
|
|
|
|
|
+ if (ret.respCode !== '0000') {
|
|
|
|
|
+ throw new InternalServerErrorException(ret.respDesc)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+export { withdraw }
|