Commit 524cbc18 authored by 姜登's avatar 姜登

zmop正式服

parents
{
// 启动端口
"httpPort": 10030,
// 域名配置
"host": "http://t.51gjj.com",
"tj1": "http://tj1.51gjj.com:5118/",
// 正式服地址
"tj2": "http://tj2.51gjj.com:5118/",
// 测试服地址
"tj3": "http://tj3.51gjj.com:5118/",
"mongoUrl": "http://tv.51gjj.com",
"zhukeUrl": "http://tv.51gjj.com",
"hexinUrl": "http://td.51gjj.com",
"wlnUrl": "http://t.51gjj.com",
// 定时任务配置
"timeInterval": "00 00 00 * * *",
// log4js配置
"log4jsName": "zmop",
"log4jsConfig": {
"appenders": [
{
"type": "console"
},
{
"category": [
"console"
],
"type": "dateFile",
"filename": "./logs/",
"alwaysIncludePattern": true,
"pattern": "-yyyyMMdd.log"
}
],
"replaceConsole": true,
"levels": {
"console": "ALL"
}
},
"dbConfig": {
"connectionLimit": 50,
"host": "rm-bp1272001633qc0x9.mysql.rds.aliyuncs.com",
"user": "hexin",
"password": "gYUHszn9#q",
"database": "node_zm"
},
"dbConfigOcr": {
"connectionLimit": 50,
"host": "rm-bp1272001633qc0x9.mysql.rds.aliyuncs.com",
"user": "query",
"password": "5gqR2EQK",
"database": "ocr"
},
"dbConfigBG": {
"connectionLimit": 50,
"host": "rm-bp1272001633qc0x9.mysql.rds.aliyuncs.com",
"user": "query",
"password": "5gqR2EQK",
"database": "background"
},
"zmop_app_id": "300002663",
//"zmop_app_id": "300002565",
//"zmop_app_id": "300002796",
"entities": {
"300002565": "杭州巨业企业管理咨询有限公司",
"300002663": "杭州巨业企业管理咨询有限公司",
"300002796": "浙江容泽网络科技有限公司",
"2018051160085345": "杭州惠风网络科技有限公司",
"2018042460057138": "杭州盆钵信息技术有限公司",
"2018051560161652": "杭州靖乾网络科技有限公司"
}
}
[
{
"eventId": 5,
"desc": "每日登录",
"max_times": 1,
"times": 0,
"total": "0",
"complete": false
},
{
"eventId": 7,
"desc": "邀请好友",
"max_times": 0,
"times": 0,
"total": "0",
"complete": false
},
{
"eventId": 8,
"desc": "购买卡券",
"max_times": 3,
"times": 5,
"total": "100",
"complete": true
},
{
"eventId": 10,
"desc": "出售卡券",
"max_times": 5,
"times": 11,
"total": "220",
"complete": true
},
{
"date": "",
"delta": 0,
"complete": false,
"eventId": 3,
"desc": "社保认证"
},
{
"date": "2018-03-01",
"delta": -13,
"complete": false,
"eventId": 1,
"desc": "公积金认证"
}
]
const rp = require('request-promise'),
request = require('request'),
moment = require('moment'),
iconv = require('iconv-lite'),
fs = require('fs'),
rsa = require('./rsa'),
orm = require('../orm'),
extend = require('extend'),
log = console.log.bind(console),
error = console.error.bind(console)
function build_transaction_id() {
let t = moment().format('YYYYMMDDHHmmssSSS')
let str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
while (t.length < 32) {
t += str[Math.round(Math.random() * (str.length - 1))]
}
return t
}
// 芝麻分
async function score(app_id, auth_token, user_id) {
let transaction_id = build_transaction_id()
let data = {
app_id: app_id,
method: 'zhima.credit.score.get',
format: 'JSON',
charset: 'utf-8',
sign_type: 'RSA2',
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
version: '1.0',
auth_token: auth_token,
biz_content: JSON.stringify({transaction_id: transaction_id, product_code: 'w1010100100000000001'}),
}
data.sign = rsa.sign(data, app_id)
let p = formatUrl(data)
let resp = await rp.post({
url: p.url,
form: p.execParams,
json: true
})
let row = {
transaction_id: transaction_id,
resp: JSON.stringify(resp),
open_id: user_id,
platform: 'alipay',
method: 'score'
}
log('zmop_score resp: ', resp)
await orm.create_api_task(row)
if (resp.zhima_credit_score_get_response.code != '10000') {
return resp.zhima_credit_score_get_response
}
if (rsa.verify(resp.sign, JSON.stringify(resp.zhima_credit_score_get_response), app_id)) {
return resp.zhima_credit_score_get_response
} else {
error('API-[ alipay/score ] 验签失败| app_id: ', app_id)
throw new Error('验签失败')
}
}
// 行业关注名单
async function watch_list(app_id, auth_token, user_id) {
let transaction_id = build_transaction_id()
let data = {
app_id: app_id,
method: 'zhima.credit.watchlistii.get',
format: 'JSON',
charset: 'utf-8',
sign_type: 'RSA2',
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
version: '1.0',
auth_token: auth_token,
biz_content: JSON.stringify({transaction_id: transaction_id, product_code: 'w1010100100000000022'}),
}
data.sign = rsa.sign(data, app_id)
let p = formatUrl(data)
let resp = await rp.post({
url: p.url,
form: p.execParams,
json: true
})
let row = {
transaction_id: transaction_id,
resp: JSON.stringify(resp),
open_id: user_id,
platform: 'alipay',
method: 'watch_list'
}
log('zmop_score resp: ', resp)
await orm.create_api_task(row)
if (resp.zhima_credit_watchlistii_get_response.code != '10000') {
return resp.zhima_credit_watchlistii_get_response
}
if (rsa.verify(resp.sign, JSON.stringify(resp.zhima_credit_watchlistii_get_response), app_id)) {
return resp.zhima_credit_watchlistii_get_response
} else {
console.error('API-[ alipay/score ] 验签失败| app_id: ', app_id)
throw new Error('验签失败')
}
}
// 芝麻分普惠版
async function brief_score(app_id, cert_no, cert_name, admit_score) {
let transaction_id = build_transaction_id()
let data = {
app_id: app_id,
method: 'zhima.credit.score.brief.get',
format: 'JSON',
charset: 'utf-8',
sign_type: 'RSA2',
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
version: '1.0',
biz_content: JSON.stringify({
transaction_id: transaction_id,
product_code: 'w1010100000000002733',
cert_type: 'IDENTITY_CARD',
cert_no: cert_no,
name: cert_name,
admittance_score: admit_score,
}),
}
data.sign = rsa.sign(data, app_id)
let p = formatUrl(data)
let resp = await rp.post({
url: p.url,
form: p.execParams,
json: true
})
log('zmop_brief_score resp: ', resp)
let row = {
transaction_id: transaction_id,
resp: JSON.stringify(resp),
open_id: cert_no,
platform: 'alipay',
method: 'brief_score'
}
await orm.create_api_task(row)
if (resp.zhima_credit_score_brief_get_response.code != '10000') {
return resp.zhima_credit_score_brief_get_response
}
if (rsa.verify(resp.sign, JSON.stringify(resp.zhima_credit_score_brief_get_response), app_id)) {
return resp.zhima_credit_score_brief_get_response
} else {
error('API-[ alipay/score ] 验签失败| app_id: ', app_id)
throw new Error('验签失败')
}
}
// setTimeout(async () => {
//
// let a = await score('2018042460057138', 'authzmaBc696f4cdee00454782876eca3f31dD48', '2088802182470482', '700')
// })
// 行业关注名单普惠版
async function brief_watch_list(app_id, cert_no, cert_name) {
let transaction_id = build_transaction_id()
let data = {
app_id: app_id,
method: 'zhima.credit.watchlist.brief.get',
format: 'JSON',
charset: 'utf-8',
sign_type: 'RSA2',
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
version: '1.0',
biz_content: JSON.stringify({
transaction_id: transaction_id,
product_code: 'w1010100000000002977',
cert_type: 'IDENTITY_CARD',
cert_no: cert_no,
name: cert_name,
}),
}
data.sign = rsa.sign(data, app_id)
let p = formatUrl(data)
let resp = await rp.post({
url: p.url,
form: p.execParams,
json: true
})
let row = {
transaction_id: transaction_id,
resp: JSON.stringify(resp),
open_id: cert_no,
platform: 'alipay',
method: 'brief_watch_list'
}
log('zmop_brief_watch_list resp: ', resp)
await orm.create_api_task(row)
if (resp.zhima_credit_watchlist_brief_get_response.code != '10000') {
return resp.zhima_credit_watchlist_brief_get_response
}
if (rsa.verify(resp.sign, JSON.stringify(resp.zhima_credit_watchlist_brief_get_response), app_id)) {
return resp.zhima_credit_watchlist_brief_get_response
} else {
error('API-[ alipay/score ] 验签失败| app_id: ', app_id)
throw new Error('验签失败')
}
}
async function access_token(app_id, auth_code,) {
let data = {
app_id: app_id,
method: 'alipay.system.oauth.token',
charset: 'utf-8',
sign_type: 'RSA2',
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
version: '1.0',
grant_type: 'authorization_code',
code: auth_code
}
data.sign = rsa.sign(data, app_id)
let resp = await rp.post({
url: 'https://openapi.alipay.com/gateway.do',
json: true,
form: data
})
console.log('##### Access_token #####\n', resp)
if (resp.error_response) {
return resp.error_response
}
if (rsa.verify(resp.sign, JSON.stringify(resp.alipay_system_oauth_token_response), app_id)) {
console.log('access_token: ', resp)
return resp.alipay_system_oauth_token_response
} else {
error('API-[ alipay/access_token ] 验签失败| app_id: ', app_id)
throw new Error('验签失败')
}
}
module.exports = {
build_transaction_id,
access_token,
score,
watch_list,
brief_score,
brief_watch_list,
}
function formatUrl(params) {
let requestUrl = 'https://openapi.alipay.com/gateway.do';
// 需要放在 url 中的参数列表
const urlArgs = [
'app_id', 'method', 'format', 'charset',
'sign_type', 'sign', 'timestamp', 'version',
'notify_url', 'return_url', 'auth_token', 'app_auth_token',
];
for (const key in params) {
if (urlArgs.includes(key)) {
const val = encodeURIComponent(params[key]);
requestUrl = `${requestUrl}${requestUrl.includes('?') ? '&' : '?'}${key}=${val}`;
// 删除 postData 中对应的数据
delete params[key];
}
}
return {execParams: params, url: requestUrl};
}
// brief_watch_list('2018051160085345', '370882199212085812', '肖爱江').then(r => {
// log(r)
// }).catch(e => {
// log(e)
// })
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnv0wNm0RDlJFeOfYO2Ed
IOF1snkiUSqp5pvwX8z6+3kc/kOdhDfwrPUk4LWx+PmCiuSUyEsojBKDCwtK8kzH
4OrA3B4yG+NrjJ1060fRJsaxX+m95+2q0a7LIz2gXuAS4glWrx3i5rSDpOF9R9qw
hNjg3pExn7NZ2n/ro3cUT+Lauzqu0A9GzJ1TwbXt97XN4mx33sfbrT+NaS6UBCMS
3KUgA1kl/AUB34eWEyDbwAei/czMUfa73eeMFGqacXESEJl/qKdulB6XPEDV0Msk
tcqbL+qx8l0jFJyh9OW829l7Pu2lBqFLk/owp9HztuHSX7N9kvO1wPu+dHk/Mgdj
FQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0y38Y65ulJhzPm4BZLZ0UK98Qcmz6munK7kZTLW7il2MvErc
DgmJ2faajxDyMDKh7fDQTcj2ZlvuwrKK9UpLfnSg5444LYSZpFTkySydQe+VPBC/
/MB9bZ/JSVotM4bEjoVdbCrs4bk3A+CjTMkq8V8WlWOBOX7HwLd6kIyfR2QrUO5q
b7v8C1Jl0B81IRkCHNTZ91rpVDzcpJkbTzVTDeC/Ydsc0KQL3FAFe+nl0+pkLyEs
H/Wr1LV0d5v63wE1Dn10CYRQ9n6tsTceNPcd2NRrWTyrczKULxtf8ylds116UCDo
bxDFUx4xr44z4qyjz4omc3zUTe5vZ7lCFSKpsQIDAQABAoIBABk+U/8uaLWGi7AY
wj3huYGzmGzcyY7qE2+cjrOmvaZKjHotkKR062+MlOkpI2ozgzvIH35M2Xde4emQ
IiA0I1YYZuuHCpwWT69fHoIVvS6WjwZBElIKSHQEN7F3ABQ6teA7fPFjKDHLTBJA
lgSlv2Ze8XomDNpsl0AoLpR82sVTsmr3wjCOPxrF/b6waXZKf7GU7wo3erziJ8gr
2qLZzxgjNaMWYIAeqFiaSp0orTLDMR06oPqOlp/vxMvIUpOlPFnvfnha2FGWAmJj
QRrENSagKO2/lLo+mvZM8GG7yuLSfH4+obMf5805j2EKdTGBHSkgAhS1J6nLIJVe
Gy4axIECgYEA6kBzkGenbCXHgeB1+PpHcgwjZsMFqDBgV6bRH2J+msGTgLviBwhj
n6qMuYJdsv2eOSHUEAtcZNVn9fsZrbg8giaGF65cFmpQZKuDE1AqfdvHePM0W8Nn
uaUzz1UjGSqhMl2N/GHoKfBMCrljnvwwJ4lbphwhzMl4yiJaWmE5U8kCgYEA5skr
quZG5b3FK85CS0qR10z/EqLElxJRF9X9vuyiQ+aIRMg4GhRajMQTDo7YIXh/rD9O
myjO1KwXa62vhB3zjy7ytQyfFuoWPDMSf6860W+Tuc2a9HSJQxQzjUMbv+gLlJYf
orrSoiVoMK4VdsW7mQXl+2IqEMRaHktcZ1YgiqkCgYEA38WT37V09NWeIQ5YA/Cq
AWNhChBwvfsAfZrImE+Rm6ohis9wGsHD0UmtxqSnd43Y8MDFAFKp/1TxS8SEAjRF
8Rp16/dO1sbWZ2SEARYKkVI62eqR8LrYwjnzJTMyJR9Y/3DpatfoXO/E5FW2ubA1
G/+WAjdLfoJTldx9LWtExSkCgYA3RAlu/YIQ1ib3XIdHnG3cTxXuEohdXQeKW7ZO
GECrXfNX9VJp/GHKv9GIdPCQ41g/mVFrmpQdHbDmGkujqcB/k1CPxpv6UshMVjah
2QaOzNdlcTUgQR/1rkrCE0X76MeNJ34f6/kw1yBiN+3v6V4JQP5X+qP0K/EgvA8+
t8NHMQKBgAcoUbBKOa5njQ9tppJK3zML2Z/miJIaXwaX/9A7Wtpdqwa/0tSa7sBQ
2lYRqMV54t1/L0DqSeYzN59yOUwmX6DBsThiwx0Vg5z8WI5aVYT+690MnL0VtHLj
jlhcBNqMxRXjH7M1Fyq+i3NjBkRutthMGO66ZEjH+BKJJ7CU2DyC
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3FVEfUyu1O1th+9JeYO
6zvuxl0OVscCi9Rx9QiPYyJAugCzQYBguk+tqkNgYI00NIz/y99NsulyjCGkLnlZ
lSolKrefjKVSSWxiNcy3yA0loLDUturIxg2kR13uSzm46fPIX3fxRc37n3Y7hIio
pE+3NCUAJGnIkQFStlvVKUoh65zkf7+sygS8l6u338THsLEcq0WBABx36NrGqxP9
Olus12Jvc9MGhrgPo4Dj0OUd8IbamHDUYPDPF2oc0a5huMKNjeTeJuJEK42k5DUj
Gdn4ZLS2AQowimKt5qwKjMC2sFcqRH1hTZD9mthuNE0PXfj61Nw5KKItob5Que53
EQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw0WUygOnvIYW6bIJaK4
WzNBwF7NhIqTTk7dew/Of2PQto+iEmv2o1Tyc53gLjvMXSbG46nxsEvyXH19OPb+
A74j4LCnQl4Mg8yph2689rJnHWmdmqvw+8A6/XcCq7rjN2LlQhu5ibya3Gi41+uy
PPUPuc2B1tL0JTydxIt8bGg3wCLXmdpHxJJ8aegBRd1+3NkfEy+53/maBujmjni/
HqJ/nsFoqJ2CjRv0Oo6hSKHi50k5wc85u4+Mp0PY0FHP8PJ6qw6vUjakiWBs16vH
0uA+/JEgAftMbpvFOuM70/5blUFg6KnMPnynbD4jIwMYpsNVJSCJOmjUERZZF2n3
ewIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3FVEfUyu1O1th+9JeYO
6zvuxl0OVscCi9Rx9QiPYyJAugCzQYBguk+tqkNgYI00NIz/y99NsulyjCGkLnlZ
lSolKrefjKVSSWxiNcy3yA0loLDUturIxg2kR13uSzm46fPIX3fxRc37n3Y7hIio
pE+3NCUAJGnIkQFStlvVKUoh65zkf7+sygS8l6u338THsLEcq0WBABx36NrGqxP9
Olus12Jvc9MGhrgPo4Dj0OUd8IbamHDUYPDPF2oc0a5huMKNjeTeJuJEK42k5DUj
Gdn4ZLS2AQowimKt5qwKjMC2sFcqRH1hTZD9mthuNE0PXfj61Nw5KKItob5Que53
EQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw0WUygOnvIYW6bIJaK4
WzNBwF7NhIqTTk7dew/Of2PQto+iEmv2o1Tyc53gLjvMXSbG46nxsEvyXH19OPb+
A74j4LCnQl4Mg8yph2689rJnHWmdmqvw+8A6/XcCq7rjN2LlQhu5ibya3Gi41+uy
PPUPuc2B1tL0JTydxIt8bGg3wCLXmdpHxJJ8aegBRd1+3NkfEy+53/maBujmjni/
HqJ/nsFoqJ2CjRv0Oo6hSKHi50k5wc85u4+Mp0PY0FHP8PJ6qw6vUjakiWBs16vH
0uA+/JEgAftMbpvFOuM70/5blUFg6KnMPnynbD4jIwMYpsNVJSCJOmjUERZZF2n3
ewIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3FVEfUyu1O1th+9JeYO
6zvuxl0OVscCi9Rx9QiPYyJAugCzQYBguk+tqkNgYI00NIz/y99NsulyjCGkLnlZ
lSolKrefjKVSSWxiNcy3yA0loLDUturIxg2kR13uSzm46fPIX3fxRc37n3Y7hIio
pE+3NCUAJGnIkQFStlvVKUoh65zkf7+sygS8l6u338THsLEcq0WBABx36NrGqxP9
Olus12Jvc9MGhrgPo4Dj0OUd8IbamHDUYPDPF2oc0a5huMKNjeTeJuJEK42k5DUj
Gdn4ZLS2AQowimKt5qwKjMC2sFcqRH1hTZD9mthuNE0PXfj61Nw5KKItob5Que53
EQIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnv0wNm0RDlJFeOfYO2Ed
IOF1snkiUSqp5pvwX8z6+3kc/kOdhDfwrPUk4LWx+PmCiuSUyEsojBKDCwtK8kzH
4OrA3B4yG+NrjJ1060fRJsaxX+m95+2q0a7LIz2gXuAS4glWrx3i5rSDpOF9R9qw
hNjg3pExn7NZ2n/ro3cUT+Lauzqu0A9GzJ1TwbXt97XN4mx33sfbrT+NaS6UBCMS
3KUgA1kl/AUB34eWEyDbwAei/czMUfa73eeMFGqacXESEJl/qKdulB6XPEDV0Msk
tcqbL+qx8l0jFJyh9OW829l7Pu2lBqFLk/owp9HztuHSX7N9kvO1wPu+dHk/Mgdj
FQIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjsvZWgV2CJMVzmWYiptw
zG3pO9qd56Zd25tKxE1FV5cP3Dfb7V5Shxf7/npkZrCdsRsD4KSFKgJYc98xQtTA
8mrCL7bwrDsNe1HExSdcPAehJ7qMgoom4OOfIgMtjsNsWdJdNLcnUHOmWLT2xrNT
2NjJuSZM+iw3xezmH4GwtpdkAG86bdf7xBeOFm4pf+8Lhi8zpZVQrYwg92npihvU
Rh/43YRFC85JjWTLeixLdEDidKTPPEg90tC2AMLAbZ/1Zk64k7EKfmLmALLggUAX
FMq4QAkWgnXm2LzzVZfbtsPH/Q/nNL/GnnZhpSq36lEqtIn8puNtXefeB+Tpx5B0
pwIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3FVEfUyu1O1th+9JeYO
6zvuxl0OVscCi9Rx9QiPYyJAugCzQYBguk+tqkNgYI00NIz/y99NsulyjCGkLnlZ
lSolKrefjKVSSWxiNcy3yA0loLDUturIxg2kR13uSzm46fPIX3fxRc37n3Y7hIio
pE+3NCUAJGnIkQFStlvVKUoh65zkf7+sygS8l6u338THsLEcq0WBABx36NrGqxP9
Olus12Jvc9MGhrgPo4Dj0OUd8IbamHDUYPDPF2oc0a5huMKNjeTeJuJEK42k5DUj
Gdn4ZLS2AQowimKt5qwKjMC2sFcqRH1hTZD9mthuNE0PXfj61Nw5KKItob5Que53
EQIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3FVEfUyu1O1th+9JeYO
6zvuxl0OVscCi9Rx9QiPYyJAugCzQYBguk+tqkNgYI00NIz/y99NsulyjCGkLnlZ
lSolKrefjKVSSWxiNcy3yA0loLDUturIxg2kR13uSzm46fPIX3fxRc37n3Y7hIio
pE+3NCUAJGnIkQFStlvVKUoh65zkf7+sygS8l6u338THsLEcq0WBABx36NrGqxP9
Olus12Jvc9MGhrgPo4Dj0OUd8IbamHDUYPDPF2oc0a5huMKNjeTeJuJEK42k5DUj
Gdn4ZLS2AQowimKt5qwKjMC2sFcqRH1hTZD9mthuNE0PXfj61Nw5KKItob5Que53
EQIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjsvZWgV2CJMVzmWYiptw
zG3pO9qd56Zd25tKxE1FV5cP3Dfb7V5Shxf7/npkZrCdsRsD4KSFKgJYc98xQtTA
8mrCL7bwrDsNe1HExSdcPAehJ7qMgoom4OOfIgMtjsNsWdJdNLcnUHOmWLT2xrNT
2NjJuSZM+iw3xezmH4GwtpdkAG86bdf7xBeOFm4pf+8Lhi8zpZVQrYwg92npihvU
Rh/43YRFC85JjWTLeixLdEDidKTPPEg90tC2AMLAbZ/1Zk64k7EKfmLmALLggUAX
FMq4QAkWgnXm2LzzVZfbtsPH/Q/nNL/GnnZhpSq36lEqtIn8puNtXefeB+Tpx5B0
pwIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjsvZWgV2CJMVzmWYiptw
zG3pO9qd56Zd25tKxE1FV5cP3Dfb7V5Shxf7/npkZrCdsRsD4KSFKgJYc98xQtTA
8mrCL7bwrDsNe1HExSdcPAehJ7qMgoom4OOfIgMtjsNsWdJdNLcnUHOmWLT2xrNT
2NjJuSZM+iw3xezmH4GwtpdkAG86bdf7xBeOFm4pf+8Lhi8zpZVQrYwg92npihvU
Rh/43YRFC85JjWTLeixLdEDidKTPPEg90tC2AMLAbZ/1Zk64k7EKfmLmALLggUAX
FMq4QAkWgnXm2LzzVZfbtsPH/Q/nNL/GnnZhpSq36lEqtIn8puNtXefeB+Tpx5B0
pwIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw0WUygOnvIYW6bIJaK4
WzNBwF7NhIqTTk7dew/Of2PQto+iEmv2o1Tyc53gLjvMXSbG46nxsEvyXH19OPb+
A74j4LCnQl4Mg8yph2689rJnHWmdmqvw+8A6/XcCq7rjN2LlQhu5ibya3Gi41+uy
PPUPuc2B1tL0JTydxIt8bGg3wCLXmdpHxJJ8aegBRd1+3NkfEy+53/maBujmjni/
HqJ/nsFoqJ2CjRv0Oo6hSKHi50k5wc85u4+Mp0PY0FHP8PJ6qw6vUjakiWBs16vH
0uA+/JEgAftMbpvFOuM70/5blUFg6KnMPnynbD4jIwMYpsNVJSCJOmjUERZZF2n3
ewIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw0WUygOnvIYW6bIJaK4
WzNBwF7NhIqTTk7dew/Of2PQto+iEmv2o1Tyc53gLjvMXSbG46nxsEvyXH19OPb+
A74j4LCnQl4Mg8yph2689rJnHWmdmqvw+8A6/XcCq7rjN2LlQhu5ibya3Gi41+uy
PPUPuc2B1tL0JTydxIt8bGg3wCLXmdpHxJJ8aegBRd1+3NkfEy+53/maBujmjni/
HqJ/nsFoqJ2CjRv0Oo6hSKHi50k5wc85u4+Mp0PY0FHP8PJ6qw6vUjakiWBs16vH
0uA+/JEgAftMbpvFOuM70/5blUFg6KnMPnynbD4jIwMYpsNVJSCJOmjUERZZF2n3
ewIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjsvZWgV2CJMVzmWYiptw
zG3pO9qd56Zd25tKxE1FV5cP3Dfb7V5Shxf7/npkZrCdsRsD4KSFKgJYc98xQtTA
8mrCL7bwrDsNe1HExSdcPAehJ7qMgoom4OOfIgMtjsNsWdJdNLcnUHOmWLT2xrNT
2NjJuSZM+iw3xezmH4GwtpdkAG86bdf7xBeOFm4pf+8Lhi8zpZVQrYwg92npihvU
Rh/43YRFC85JjWTLeixLdEDidKTPPEg90tC2AMLAbZ/1Zk64k7EKfmLmALLggUAX
FMq4QAkWgnXm2LzzVZfbtsPH/Q/nNL/GnnZhpSq36lEqtIn8puNtXefeB+Tpx5B0
pwIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw0WUygOnvIYW6bIJaK4
WzNBwF7NhIqTTk7dew/Of2PQto+iEmv2o1Tyc53gLjvMXSbG46nxsEvyXH19OPb+
A74j4LCnQl4Mg8yph2689rJnHWmdmqvw+8A6/XcCq7rjN2LlQhu5ibya3Gi41+uy
PPUPuc2B1tL0JTydxIt8bGg3wCLXmdpHxJJ8aegBRd1+3NkfEy+53/maBujmjni/
HqJ/nsFoqJ2CjRv0Oo6hSKHi50k5wc85u4+Mp0PY0FHP8PJ6qw6vUjakiWBs16vH
0uA+/JEgAftMbpvFOuM70/5blUFg6KnMPnynbD4jIwMYpsNVJSCJOmjUERZZF2n3
ewIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsw0WUygOnvIYW6bIJaK4
WzNBwF7NhIqTTk7dew/Of2PQto+iEmv2o1Tyc53gLjvMXSbG46nxsEvyXH19OPb+
A74j4LCnQl4Mg8yph2689rJnHWmdmqvw+8A6/XcCq7rjN2LlQhu5ibya3Gi41+uy
PPUPuc2B1tL0JTydxIt8bGg3wCLXmdpHxJJ8aegBRd1+3NkfEy+53/maBujmjni/
HqJ/nsFoqJ2CjRv0Oo6hSKHi50k5wc85u4+Mp0PY0FHP8PJ6qw6vUjakiWBs16vH
0uA+/JEgAftMbpvFOuM70/5blUFg6KnMPnynbD4jIwMYpsNVJSCJOmjUERZZF2n3
ewIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjsvZWgV2CJMVzmWYiptw
zG3pO9qd56Zd25tKxE1FV5cP3Dfb7V5Shxf7/npkZrCdsRsD4KSFKgJYc98xQtTA
8mrCL7bwrDsNe1HExSdcPAehJ7qMgoom4OOfIgMtjsNsWdJdNLcnUHOmWLT2xrNT
2NjJuSZM+iw3xezmH4GwtpdkAG86bdf7xBeOFm4pf+8Lhi8zpZVQrYwg92npihvU
Rh/43YRFC85JjWTLeixLdEDidKTPPEg90tC2AMLAbZ/1Zk64k7EKfmLmALLggUAX
FMq4QAkWgnXm2LzzVZfbtsPH/Q/nNL/GnnZhpSq36lEqtIn8puNtXefeB+Tpx5B0
pwIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjsvZWgV2CJMVzmWYiptw
zG3pO9qd56Zd25tKxE1FV5cP3Dfb7V5Shxf7/npkZrCdsRsD4KSFKgJYc98xQtTA
8mrCL7bwrDsNe1HExSdcPAehJ7qMgoom4OOfIgMtjsNsWdJdNLcnUHOmWLT2xrNT
2NjJuSZM+iw3xezmH4GwtpdkAG86bdf7xBeOFm4pf+8Lhi8zpZVQrYwg92npihvU
Rh/43YRFC85JjWTLeixLdEDidKTPPEg90tC2AMLAbZ/1Zk64k7EKfmLmALLggUAX
FMq4QAkWgnXm2LzzVZfbtsPH/Q/nNL/GnnZhpSq36lEqtIn8puNtXefeB+Tpx5B0
pwIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnv0wNm0RDlJFeOfYO2Ed
IOF1snkiUSqp5pvwX8z6+3kc/kOdhDfwrPUk4LWx+PmCiuSUyEsojBKDCwtK8kzH
4OrA3B4yG+NrjJ1060fRJsaxX+m95+2q0a7LIz2gXuAS4glWrx3i5rSDpOF9R9qw
hNjg3pExn7NZ2n/ro3cUT+Lauzqu0A9GzJ1TwbXt97XN4mx33sfbrT+NaS6UBCMS
3KUgA1kl/AUB34eWEyDbwAei/czMUfa73eeMFGqacXESEJl/qKdulB6XPEDV0Msk
tcqbL+qx8l0jFJyh9OW829l7Pu2lBqFLk/owp9HztuHSX7N9kvO1wPu+dHk/Mgdj
FQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnv0wNm0RDlJFeOfYO2Ed
IOF1snkiUSqp5pvwX8z6+3kc/kOdhDfwrPUk4LWx+PmCiuSUyEsojBKDCwtK8kzH
4OrA3B4yG+NrjJ1060fRJsaxX+m95+2q0a7LIz2gXuAS4glWrx3i5rSDpOF9R9qw
hNjg3pExn7NZ2n/ro3cUT+Lauzqu0A9GzJ1TwbXt97XN4mx33sfbrT+NaS6UBCMS
3KUgA1kl/AUB34eWEyDbwAei/czMUfa73eeMFGqacXESEJl/qKdulB6XPEDV0Msk
tcqbL+qx8l0jFJyh9OW829l7Pu2lBqFLk/owp9HztuHSX7N9kvO1wPu+dHk/Mgdj
FQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0y38Y65ulJhzPm4BZLZ0UK98Qcmz6munK7kZTLW7il2MvErc
DgmJ2faajxDyMDKh7fDQTcj2ZlvuwrKK9UpLfnSg5444LYSZpFTkySydQe+VPBC/
/MB9bZ/JSVotM4bEjoVdbCrs4bk3A+CjTMkq8V8WlWOBOX7HwLd6kIyfR2QrUO5q
b7v8C1Jl0B81IRkCHNTZ91rpVDzcpJkbTzVTDeC/Ydsc0KQL3FAFe+nl0+pkLyEs
H/Wr1LV0d5v63wE1Dn10CYRQ9n6tsTceNPcd2NRrWTyrczKULxtf8ylds116UCDo
bxDFUx4xr44z4qyjz4omc3zUTe5vZ7lCFSKpsQIDAQABAoIBABk+U/8uaLWGi7AY
wj3huYGzmGzcyY7qE2+cjrOmvaZKjHotkKR062+MlOkpI2ozgzvIH35M2Xde4emQ
IiA0I1YYZuuHCpwWT69fHoIVvS6WjwZBElIKSHQEN7F3ABQ6teA7fPFjKDHLTBJA
lgSlv2Ze8XomDNpsl0AoLpR82sVTsmr3wjCOPxrF/b6waXZKf7GU7wo3erziJ8gr
2qLZzxgjNaMWYIAeqFiaSp0orTLDMR06oPqOlp/vxMvIUpOlPFnvfnha2FGWAmJj
QRrENSagKO2/lLo+mvZM8GG7yuLSfH4+obMf5805j2EKdTGBHSkgAhS1J6nLIJVe
Gy4axIECgYEA6kBzkGenbCXHgeB1+PpHcgwjZsMFqDBgV6bRH2J+msGTgLviBwhj
n6qMuYJdsv2eOSHUEAtcZNVn9fsZrbg8giaGF65cFmpQZKuDE1AqfdvHePM0W8Nn
uaUzz1UjGSqhMl2N/GHoKfBMCrljnvwwJ4lbphwhzMl4yiJaWmE5U8kCgYEA5skr
quZG5b3FK85CS0qR10z/EqLElxJRF9X9vuyiQ+aIRMg4GhRajMQTDo7YIXh/rD9O
myjO1KwXa62vhB3zjy7ytQyfFuoWPDMSf6860W+Tuc2a9HSJQxQzjUMbv+gLlJYf
orrSoiVoMK4VdsW7mQXl+2IqEMRaHktcZ1YgiqkCgYEA38WT37V09NWeIQ5YA/Cq
AWNhChBwvfsAfZrImE+Rm6ohis9wGsHD0UmtxqSnd43Y8MDFAFKp/1TxS8SEAjRF
8Rp16/dO1sbWZ2SEARYKkVI62eqR8LrYwjnzJTMyJR9Y/3DpatfoXO/E5FW2ubA1
G/+WAjdLfoJTldx9LWtExSkCgYA3RAlu/YIQ1ib3XIdHnG3cTxXuEohdXQeKW7ZO
GECrXfNX9VJp/GHKv9GIdPCQ41g/mVFrmpQdHbDmGkujqcB/k1CPxpv6UshMVjah
2QaOzNdlcTUgQR/1rkrCE0X76MeNJ34f6/kw1yBiN+3v6V4JQP5X+qP0K/EgvA8+
t8NHMQKBgAcoUbBKOa5njQ9tppJK3zML2Z/miJIaXwaX/9A7Wtpdqwa/0tSa7sBQ
2lYRqMV54t1/L0DqSeYzN59yOUwmX6DBsThiwx0Vg5z8WI5aVYT+690MnL0VtHLj
jlhcBNqMxRXjH7M1Fyq+i3NjBkRutthMGO66ZEjH+BKJJ7CU2DyC
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnv0wNm0RDlJFeOfYO2Ed
IOF1snkiUSqp5pvwX8z6+3kc/kOdhDfwrPUk4LWx+PmCiuSUyEsojBKDCwtK8kzH
4OrA3B4yG+NrjJ1060fRJsaxX+m95+2q0a7LIz2gXuAS4glWrx3i5rSDpOF9R9qw
hNjg3pExn7NZ2n/ro3cUT+Lauzqu0A9GzJ1TwbXt97XN4mx33sfbrT+NaS6UBCMS
3KUgA1kl/AUB34eWEyDbwAei/czMUfa73eeMFGqacXESEJl/qKdulB6XPEDV0Msk
tcqbL+qx8l0jFJyh9OW829l7Pu2lBqFLk/owp9HztuHSX7N9kvO1wPu+dHk/Mgdj
FQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0y38Y65ulJhzPm4BZLZ0
UK98Qcmz6munK7kZTLW7il2MvErcDgmJ2faajxDyMDKh7fDQTcj2ZlvuwrKK9UpL
fnSg5444LYSZpFTkySydQe+VPBC//MB9bZ/JSVotM4bEjoVdbCrs4bk3A+CjTMkq
8V8WlWOBOX7HwLd6kIyfR2QrUO5qb7v8C1Jl0B81IRkCHNTZ91rpVDzcpJkbTzVT
DeC/Ydsc0KQL3FAFe+nl0+pkLyEsH/Wr1LV0d5v63wE1Dn10CYRQ9n6tsTceNPcd
2NRrWTyrczKULxtf8ylds116UCDobxDFUx4xr44z4qyjz4omc3zUTe5vZ7lCFSKp
sQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0y38Y65ulJhzPm4BZLZ0UK98Qcmz6munK7kZTLW7il2MvErc
DgmJ2faajxDyMDKh7fDQTcj2ZlvuwrKK9UpLfnSg5444LYSZpFTkySydQe+VPBC/
/MB9bZ/JSVotM4bEjoVdbCrs4bk3A+CjTMkq8V8WlWOBOX7HwLd6kIyfR2QrUO5q
b7v8C1Jl0B81IRkCHNTZ91rpVDzcpJkbTzVTDeC/Ydsc0KQL3FAFe+nl0+pkLyEs
H/Wr1LV0d5v63wE1Dn10CYRQ9n6tsTceNPcd2NRrWTyrczKULxtf8ylds116UCDo
bxDFUx4xr44z4qyjz4omc3zUTe5vZ7lCFSKpsQIDAQABAoIBABk+U/8uaLWGi7AY
wj3huYGzmGzcyY7qE2+cjrOmvaZKjHotkKR062+MlOkpI2ozgzvIH35M2Xde4emQ
IiA0I1YYZuuHCpwWT69fHoIVvS6WjwZBElIKSHQEN7F3ABQ6teA7fPFjKDHLTBJA
lgSlv2Ze8XomDNpsl0AoLpR82sVTsmr3wjCOPxrF/b6waXZKf7GU7wo3erziJ8gr
2qLZzxgjNaMWYIAeqFiaSp0orTLDMR06oPqOlp/vxMvIUpOlPFnvfnha2FGWAmJj
QRrENSagKO2/lLo+mvZM8GG7yuLSfH4+obMf5805j2EKdTGBHSkgAhS1J6nLIJVe
Gy4axIECgYEA6kBzkGenbCXHgeB1+PpHcgwjZsMFqDBgV6bRH2J+msGTgLviBwhj
n6qMuYJdsv2eOSHUEAtcZNVn9fsZrbg8giaGF65cFmpQZKuDE1AqfdvHePM0W8Nn
uaUzz1UjGSqhMl2N/GHoKfBMCrljnvwwJ4lbphwhzMl4yiJaWmE5U8kCgYEA5skr
quZG5b3FK85CS0qR10z/EqLElxJRF9X9vuyiQ+aIRMg4GhRajMQTDo7YIXh/rD9O
myjO1KwXa62vhB3zjy7ytQyfFuoWPDMSf6860W+Tuc2a9HSJQxQzjUMbv+gLlJYf
orrSoiVoMK4VdsW7mQXl+2IqEMRaHktcZ1YgiqkCgYEA38WT37V09NWeIQ5YA/Cq
AWNhChBwvfsAfZrImE+Rm6ohis9wGsHD0UmtxqSnd43Y8MDFAFKp/1TxS8SEAjRF
8Rp16/dO1sbWZ2SEARYKkVI62eqR8LrYwjnzJTMyJR9Y/3DpatfoXO/E5FW2ubA1
G/+WAjdLfoJTldx9LWtExSkCgYA3RAlu/YIQ1ib3XIdHnG3cTxXuEohdXQeKW7ZO
GECrXfNX9VJp/GHKv9GIdPCQ41g/mVFrmpQdHbDmGkujqcB/k1CPxpv6UshMVjah
2QaOzNdlcTUgQR/1rkrCE0X76MeNJ34f6/kw1yBiN+3v6V4JQP5X+qP0K/EgvA8+
t8NHMQKBgAcoUbBKOa5njQ9tppJK3zML2Z/miJIaXwaX/9A7Wtpdqwa/0tSa7sBQ
2lYRqMV54t1/L0DqSeYzN59yOUwmX6DBsThiwx0Vg5z8WI5aVYT+690MnL0VtHLj
jlhcBNqMxRXjH7M1Fyq+i3NjBkRutthMGO66ZEjH+BKJJ7CU2DyC
-----END RSA PRIVATE KEY-----
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
const NodeRSA = require('node-rsa');
// def sign(privatekey, data):
// '''
// 加签
// :param privatekey:私钥文件路径
// :param data:要加签的数据
// :return string:签名
// '''
// signEVP = EVP.load_key(privatekey)
// signEVP.sign_init()
// signEVP.sign_update(data)
// result = signEVP.sign_final()
// return base64.b64encode(result)
const private_key = fs.readFileSync(path.join(__dirname, 'keys/private_key.pem')).toString()
const ant_public_key = {}
ant_public_key['2018042460057138'] = fs.readFileSync(path.join(__dirname, 'keys/2018042460057138_ant_public_key.pem')).toString()
ant_public_key['2018051160085345'] = fs.readFileSync(path.join(__dirname, 'keys/2018051160085345_ant_public_key.pem')).toString()
ant_public_key['2018051560161652'] = fs.readFileSync(path.join(__dirname, 'keys/2018051560161652_ant_public_key.pem')).toString()
ant_public_key['2018060760276862'] = fs.readFileSync(path.join(__dirname, 'keys/2018060760276862_ant_public_key.pem')).toString()
ant_public_key['2018062860440367'] = fs.readFileSync(path.join(__dirname, 'keys/2018062860440367_ant_public_key.pem')).toString()
ant_public_key['2018062860428388'] = fs.readFileSync(path.join(__dirname, 'keys/2018062860428388_ant_public_key.pem')).toString()
ant_public_key['2018070560531397'] = fs.readFileSync(path.join(__dirname, 'keys/2018070560531397_ant_public_key.pem')).toString()
ant_public_key['2018072660760379'] = fs.readFileSync(path.join(__dirname, 'keys/2018072660760379_ant_public_key.pem')).toString()
ant_public_key['2018062660437189'] = fs.readFileSync(path.join(__dirname, 'keys/2018062660437189_ant_public_key.pem')).toString()
ant_public_key['2018080760936372'] = fs.readFileSync(path.join(__dirname, 'keys/2018080760936372_ant_public_key.pem')).toString()
ant_public_key['2018062960509010'] = fs.readFileSync(path.join(__dirname, 'keys/2018062960509010_ant_public_key.pem')).toString()
ant_public_key['2018092861524698'] = fs.readFileSync(path.join(__dirname, 'keys/2018092861524698_ant_public_key.pem')).toString()
ant_public_key['2018092861534661'] = fs.readFileSync(path.join(__dirname, 'keys/2018092861534661_ant_public_key.pem')).toString()
ant_public_key['2018092961567412'] = fs.readFileSync(path.join(__dirname, 'keys/2018092961567412_ant_public_key.pem')).toString()
ant_public_key['2018092961498814'] = fs.readFileSync(path.join(__dirname, 'keys/2018092961498814_ant_public_key.pem')).toString()
ant_public_key['2018092961519845'] = fs.readFileSync(path.join(__dirname, 'keys/2018092961519845_ant_public_key.pem')).toString()
ant_public_key['2018092961513806'] = fs.readFileSync(path.join(__dirname, 'keys/2018092961513806_ant_public_key.pem')).toString()
ant_public_key['2018092961495835'] = fs.readFileSync(path.join(__dirname, 'keys/2018092961495835_ant_public_key.pem')).toString()
ant_public_key['2018092961504848'] = fs.readFileSync(path.join(__dirname, 'keys/2018092961504848_ant_public_key.pem')).toString()
//params= URLEncode(Base64(RSAEncrypt(param1=URLEncode(param1value)&param2=URLEncode(param2value))))
// 加签
// :param privatekey:私钥文件路径
// :param data:要加签的数据
// :return string:签名
function RSAEncrypt(data) {
let str = params2str(data)
let key = new NodeRSA(private_key)
key.setOptions({encryptionScheme: 'pkcs1'});
let encrypted = key.encrypt(str, 'base64')
return encrypted
}
function RSADecrypt(str) {
let key = new NodeRSA(private_key)
key.setOptions({encryptionScheme: 'pkcs1'});
let encrypted = key.decrypt(str, 'utf8')
return encrypted
}
// 转换业务参数为str
function params2str(data) {
let str = ''
for (let k of Object.keys(data).sort()) {
let v = data[k]
if (Object.prototype.toString.call(v) === '[object Object]') {
v = JSON.stringify(v)
}
str += k + '=' + v + '&'
}
return str.slice(0, str.length - 1)
}
// str 转为 json
function str2params(str) {
let ret = {}
let arr = str.split('&')
for (let item of arr) {
let index = item.indexOf('=')
ret[item.slice(0, index)] = decodeURIComponent(item.slice(index + 1))
}
return ret
}
// 加签
function sign(data) {
let str = params2str(data)
let sign = crypto.createSign('RSA-SHA256')
sign.update(str)
return sign.sign(private_key, 'base64')
}
// 验签
// sign_value: 签名,
// str: 待验签的数据
function verify(sign_value, str, app_id) {
var verify = crypto.createVerify('RSA-SHA256');
verify.update(str);
return verify.verify(ant_public_key[app_id], sign_value, 'base64')
}
// let resp = { alipay_system_oauth_token_response:
// { access_token: 'authzmaBd3a9fe8c041843cdbd01cb6ba1c4fX48',
// alipay_user_id: '20881049053508330572421081610248',
// expires_in: 31536000,
// re_expires_in: 31622400,
// refresh_token: 'authzmaB6991f8567e38405c85803d5023550X48',
// user_id: '2088802182470482' },
// sign: 'LriQN3gcY177GL1SPqGqsnD1YuJdR9qKg4fSaS8V3KNXF/Fi2I0eDSK0t8o0lAHB3AgHF6YyD7+VwtO/pKc9yZpbTIVSrCm2kFaEJGaJgG7F7sh+CNEZnNE69usW50Y7LQUGPy8olr2+LnE6eyM4xR9KlYDTCP4RA+K0OlFSKz8V0ylwgC0H9nij+le3pyQZdESarLGMuZk24+a49lYgZfjsnI6rf2DkrCN1/0RNNuIGqS2XtXBuQCY15gzBIyY9ux65YIvEoc+LUKTyNOpXvBeQYP9JzkQqc7Nkh1f6lozcrNtoxdP8ltZU/dW3/2TpIQ4oQ1J2ztezmamZvTxjDg==' }
// if (verify(resp.sign, JSON.stringify(resp.alipay_system_oauth_token_response), '2018051560161652')) {
// console.log('success')
// } else {
// console.log('error')
// }
// let a = {
// apiname: 'com.alipay.account.auth',
// method: 'alipay.open.auth.sdk.code.get',
// app_id: 2018042460057138,
// app_name: 'mc',
// biz_type: 'openservice',
// pid: 2088031974398640,
// product_id: 'APP_FAST_LOGIN',
// scope: 'kuaijie',
// target_id: 'kkkkk091125',
// auth_type: 'AUTHACCOUNT',
// sign_type: 'RSA2',
// }
// a.sign = encodeURIComponent(sign(a))
// console.log(sign('app_id=2018042460057138&amp;charset=utf-8&amp;code=1558df8e1bd2409dafeb9f428075SX48&amp;format=JSON&amp;grant_type=authorization_code&amp;method=alipay.system.oauth.token&amp;sign_type=RSA2&amp;timestamp=2018-05-03 16:40:45&amp;version=1.0'));
module.exports = {
sign,
verify,
RSAEncrypt,
RSADecrypt,
params2str,
str2params
}
This diff is collapsed.
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCir0A1PyJ+v56i21m/hjECVtM43oq7Uj8SC1lDhJqeOWMCZEQc
ChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5GcyX68VPBTeRml7wtp/0oYoW1c
9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+T4R2LdwbaK2hUnKdFwIDAQAB
AoGAJlbw5duUMJTOOQlC1G02nZuCufNQdKcEcCc2iT/2BHX3zHd+ybDh1dIcuhhU
2cx231W+CGi60ikljH1gXcWu0GAbGMSyvC4sTs+sDNpzp2peYtZ67bSNp7rd6sNA
v8zZgC66zpNQVuD0WQNxHAISQNUNNaxDGz+AYRIBM4VKy7kCQQDOnWsEHKdr12f5
1GgiownfoC53U+oRiyuoxFx+YS7iR9QCAPRHx8822LgThMA1qvINKfDwtocpK+UO
imi2MyHNAkEAyZHGRPO+ij9XRIw5IfFNpGpt40MQv4PQYpmI/of7zbBVUaEFvJ0y
DZ6s9KhU7cHblKsruUHu6KZS/Lu37k4mcwJAQNf2thuoD5hS8X1QEU7J0n0bGCgl
1cuvsA+wV5l4dVvFlbtm7wAucDAj1Txcdntn6+m1zFX8pVc5VA7sPgJeIQJAG1SO
o47BCRGpjXvNy1JiiqZ7m9LeFHmU6amTr59UfwEnsFe65WYTAVHAdbPVQB+G3FOR
LB9Ke3UBz47MU515DQJAMeAIdlp71W1TKNhLv5pLQtfSmFzMQWvhXZuLnysvqFfg
IJpsio6hJcr9sJI72s6kBPaVyVvwCYOlreXq2SM2uw==
-----END RSA PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCir0A1PyJ+v56i21m/hjECVtM43oq7Uj8SC1lDhJqeOWMCZEQc
ChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5GcyX68VPBTeRml7wtp/0oYoW1c
9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+T4R2LdwbaK2hUnKdFwIDAQAB
AoGAJlbw5duUMJTOOQlC1G02nZuCufNQdKcEcCc2iT/2BHX3zHd+ybDh1dIcuhhU
2cx231W+CGi60ikljH1gXcWu0GAbGMSyvC4sTs+sDNpzp2peYtZ67bSNp7rd6sNA
v8zZgC66zpNQVuD0WQNxHAISQNUNNaxDGz+AYRIBM4VKy7kCQQDOnWsEHKdr12f5
1GgiownfoC53U+oRiyuoxFx+YS7iR9QCAPRHx8822LgThMA1qvINKfDwtocpK+UO
imi2MyHNAkEAyZHGRPO+ij9XRIw5IfFNpGpt40MQv4PQYpmI/of7zbBVUaEFvJ0y
DZ6s9KhU7cHblKsruUHu6KZS/Lu37k4mcwJAQNf2thuoD5hS8X1QEU7J0n0bGCgl
1cuvsA+wV5l4dVvFlbtm7wAucDAj1Txcdntn6+m1zFX8pVc5VA7sPgJeIQJAG1SO
o47BCRGpjXvNy1JiiqZ7m9LeFHmU6amTr59UfwEnsFe65WYTAVHAdbPVQB+G3FOR
LB9Ke3UBz47MU515DQJAMeAIdlp71W1TKNhLv5pLQtfSmFzMQWvhXZuLnysvqFfg
IJpsio6hJcr9sJI72s6kBPaVyVvwCYOlreXq2SM2uw==
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCir0A1PyJ+v56i21m/hjECVtM4
3oq7Uj8SC1lDhJqeOWMCZEQcChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5Gc
yX68VPBTeRml7wtp/0oYoW1c9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+
T4R2LdwbaK2hUnKdFwIDAQAB
-----END PUBLIC KEY-----
tt.51gjj.com:5000/redirect?params=JevbQqScHiM1Qoryf1%2FD4MIrl7mvWRtt8neRUzLelEdru%2FRUt8N%2FQOs3qWgdOqUXVNXZnUL87Bc0uFFm4dxAwRRNxr0ek4kFPBoDwuk063AS3vFz%2Bsm0wy808hX6s63MnXfCbGqq1tpvkVb4mFp1jDFXJJgZjOoJ%2FBhTLfQWI%2BSE4YjWcvm3%2BV9enCv8TrYZ3jPhmf%2F3KeX%2BuZB3HhWmnqQHv6nu9Wbh%2Fqf2sevtvWzV08F6eT8JcDhF%2FK%2F1aZvSVgr83%2BuzRbO%2BCUa7KY0GWNc5moFfhT45TeR2PslcTDde36sUumlLdEQxR2qXXf7TPL2z0Y0bWKE7JMpbx%2BqnPg%3D%3D&sign=HOg8UcgLT4zFTq5J7sTy9N7XHjS%2Fg8NfMnYoR4ip9UdPPScHXiqnuIDZ44zQT9NU5X6z3XoeGt04YFfeDS3DXGaD1YhyTvWwCigAMPnu7YlSG1ZeAputY0xD8sCkN7Dv20GQE42%2BU%2BSOQk8wzcsHJ87KwQUkXF9oTQviaIS0fk0%3D
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyM+GAgdFQebzSWf7laNMxwdR6
SzNW8pouEIyODY4VoEaM2YE3Lye75jBNxHT5+4GCI0SGyLG/hvaZSfYkLWjEpai+
3UJ914q9rFtLpoiRDYwxBpEhC+1fQrE2D3jRJi1ON/VOQ6nUU8sDTKBYSqBXzUBJ
qLgvyPSNC6xJjIBWZQIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
This diff is collapsed.
const OSS = require('ali-oss')
const co = require('co')
const crypto = require('crypto')
const fs = require('fs')
const config = require('config')
const client = new OSS({
bucket: '51query',
accessKeyId: 'LTAI6Glaum6TDzKf',
accessKeySecret: 'zmNjWhmOD1nIClZ3hoDmGrdNNiBJBi',
secure: true,
region: "oss-cn-hangzhou"
})
function upload_image(key, data) {
return co(function*() {
// use 'chunked encoding'
//var stream = fs.createReadStream('./exp.js');
data = data.replace(/^data:image\/\S+?;base64,/,"");
var result = yield client.put(key, new Buffer(data, 'base64'));
return result
}).catch(function (err) {
console.log('upload_image error: ', err);
});
}
function upload(key, data) {
return co(function*() {
// use 'chunked encoding'
//var stream = fs.createReadStream('./exp.js');
var result = yield client.put(key, new Buffer(data));
return result
}).catch(function (err) {
console.log('upload error: ', err);
});
}
const getUploadSign = (dirPath, fileName) => {
const {AccessKeyId, Host, AccessKeySecret} = config.oss
let callback = {
"callbackUrl": `${config.host}/kqyx/oss/callback`,
"callbackBody": "filename=${object}&size=${size}&mimeType=${mimeType}",
"callbackBodyType": "application/x-www-form-urlencoded"
}
callback = JSON.stringify(callback)
const callbackbody = new Buffer(callback).toString('base64')
const end = new Date().getTime() + 300000
const expiration = new Date(end).toISOString()
let policyString = {
expiration,
conditions: [
// 1024*1024 1mb限制
['content-length-range', 0, 1048576],
['starts-with', '$key', 'kqyx']
]
}
policyString = JSON.stringify(policyString)
const policy = new Buffer(policyString).toString('base64')
const signature = crypto.createHmac('sha1', AccessKeySecret).update(policy).digest('base64')
return {
AccessKeyId,
host: Host,
policy,
signature,
fileName,
dirPath,
callbackbody
}
}
const signed_url = (name) => {
return client.signatureUrl(name)
}
const deleteFile = (key) => {
co(function*() {
return yield client.delete(key)
}).catch(function (err) {
console.log(err)
throw err
})
}
module.exports = {
upload_image,
signed_url,
deleteFile,
upload
}
\ No newline at end of file
This diff is collapsed.
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
const NodeRSA = require('node-rsa');
// def sign(privatekey, data):
// '''
// 加签
// :param privatekey:私钥文件路径
// :param data:要加签的数据
// :return string:签名
// '''
// signEVP = EVP.load_key(privatekey)
// signEVP.sign_init()
// signEVP.sign_update(data)
// result = signEVP.sign_final()
// return base64.b64encode(result)
const private_key = fs.readFileSync(path.join(__dirname, 'keys/rsa_private_key.pem')).toString()
const zmop_public_key = fs.readFileSync(path.join(__dirname, 'keys/zmop_public_key.pem')).toString()
//params= URLEncode(Base64(RSAEncrypt(param1=URLEncode(param1value)&param2=URLEncode(param2value))))
// 加签
// :param privatekey:私钥文件路径
// :param data:要加签的数据
// :return string:签名
function RSAEncrypt(data) {
let str = params2str(data)
let key = new NodeRSA(zmop_public_key)
key.setOptions({encryptionScheme: 'pkcs1'});
let encrypted = key.encrypt(str, 'base64')
return encrypted
}
function RSADecrypt(str) {
let key = new NodeRSA(private_key)
key.setOptions({encryptionScheme: 'pkcs1'});
let encrypted = key.decrypt(str, 'utf8')
return encrypted
}
// 转换业务参数为str
function params2str(data) {
let str = ''
for (let k of Object.keys(data)) {
let v = data[k]
if (Object.prototype.toString.call(v) === '[object Object]') {
v = JSON.stringify(v)
}
str += k + '=' + encodeURIComponent(v) + '&'
}
return str.slice(0, str.length - 1)
}
// str 转为 json
function str2params(str) {
let ret = {}
let arr = str.split('&')
for (let item of arr) {
let index = item.indexOf('=')
ret[item.slice(0, index)] = decodeURIComponent(item.slice(index + 1))
}
return ret
}
// 加签
function sign(data) {
// 签名采用sha1withRsa算法
let str = params2str(data)
let sign = crypto.createSign('RSA-SHA1')
sign.update(str)
return sign.sign(private_key, 'base64')
}
// 验签
// sign_value: 签名,
// str: 待验签的数据
function verify(sign_value, str) {
var verify = crypto.createVerify('RSA-SHA1');
verify.update(str);
return verify.verify(zmop_public_key, sign_value, 'base64')
}
// let resp = {
// "encrypted": true,
// "sign": {
// "signSource": "zhima_sign_value",
// "signResult": "OKICqUJzHMn9ee5p0xIlJE7L5FZ5nOmjtdDYCqHD3FsQCM5RLJ2U3kFCpXf4sWnSW5J/DRsCmYRffofZapP7wH//xrJq5n9rVDg+CHQ3i5E5Mrtv4/X3wsURaDXpX1wJr+mGd2RL8zTwDi9FW1N7bHK28I3sNgwaoWLeS15iJ4s="
// },
// "biz_response_sign": "cEHP5udepFpo6Ow/ejWwLR0Q1MPaxUZA17zt7Ezd73DVNUl8RqDf/mNzap79aOucOc53n0GXwY1HIQMLLNKI9w2va62SajFhrLFYMy+QtWN3w6EsnuWNOz0xQLKpKCw3HbloD2S6nYk1dg3fBWMhC2pyZ8xXES4l6nZXWzTsOnc=",
// "biz_response": "gG4WYaHXetiko/9nqvGPGDVIyY055WgIDQ3RxVEmX9xVRAO9m8rzO33G0lO8S3iQkfHnkjZhbiQhWP7Fm8x9LtEwm8GjnSl+6egPgORUazOAdKdbvRWr9ri18znCJLu5JBr9QH6J7NoPU5YV231fN4bCPRnpD603p96TPQ/Zw/k="
// }
// if (resp.encrypted) {
// let decrypted_res = RSADecrypt(resp.biz_response)
// console.log(JSON.parse(decrypted_res))
// if (verify(resp.biz_response_sign, decrypted_res)) {
// console.log('success')
// } else {
// console.log('error')
// }
// }
module.exports = {
sign,
verify,
RSAEncrypt,
RSADecrypt,
params2str,
str2params
}
/**
* Created by xaj on 2017/6/6.
*/
const schedule = require('node-schedule');
const fs = require('fs');
const path = require('path');
const moment = require('moment');
const config = require('config');
const basic = require('../middlewares/_pub').basic;
const process = require('child_process');
/*每分钟的第30秒触发: '30 * * * * *'
每小时的1分30秒触发 :'30 1 * * * *'
每天的凌晨1点1分30秒触发 :'30 1 1 * * *'
每月的1日1点1分30秒触发 :'30 1 1 1 * *'
2016年的1月1日1点1分30秒触发 :'30 1 1 1 1 2016'
每周1的1点1分30秒触发 :'30 1 1 * * 1'*/
function BeginCleanLogWork(timeInterval){
schedule.scheduleJob(timeInterval, cleanLogJob);
}
function BeginCleanPm2LogWork(){
schedule.scheduleJob('00 00 00 1 * *', cleanPm2LogJob);
}
function cleanLogJob() {
console.log('[cleanLogJob] BeginDayWork:' + new Date());
let servicName = config.get('log4jsName');
let dateMoment = moment(new Date()).subtract(3, 'days').format('YYYYMMDD');
let fileName = basic.getLocalIP('eth', 'IPv4') + '-' + servicName + '-' + dateMoment + '.log';
let filePath = path.join('./logs/' + fileName);
console.log(fileName)
console.log(filePath)
if (fs.existsSync(filePath)) {
fs.unlink(filePath, function (err) {
if (err) {
console.error('[cleanLogJob] 清除3天前日志失败,原因:' + JSON.stringify(err));
}
else {
console.log('[cleanLogJob] 清除3天前日志成功,被清除日志:' + fileName);
}
})
}
else {
console.error('[cleanLogJob] 日志文件: %s 不存在', filePath);
}
}
function cleanPm2LogJob() {
process.exec('pm2 flush', function (error, stdout, stderr) {
if (error) {
console.log('[cleanPm2LogJob] exec error: ' + error);
}
else {
console.log(stdout);
console.log(stderr);
}
});
}
// BeginCleanLogWork();
module.exports = {
"BeginCleanLogWork": BeginCleanLogWork,
"BeginCleanPm2LogWork": BeginCleanPm2LogWork
};
const moment = require('moment')
const orm = require('./orm')
function test_month(str) {
let a = which_month()
let b = which_month(str)
if (a == b) {
return true
} else {
return false
}
}
function which_month(str = null) {
let today = moment()
if (str) {
today = moment(str)
}
let day = today.get('date')
if (day > 6) {
return today.format('YYYY-MM')
} else {
return today.subtract(1, 'month').format('YYYY-MM')
}
}
async function test_order_id(order_id) {
try {
if (!order_id) {
throw new Error('无效的订单号')
}
let ret = true
let t = order_id.slice(0, 17)
let append = order_id.slice(17)
if (/[^0-9a-zA-Z_-]/.test(append) || order_id.length != 32 || /[^\d]/.test(t)) {
throw new Error('无效的订单号')
}
return await orm.check_order_id(order_id)
} catch (e) {
throw e
}
}
function to_base64(str) {
return new Buffer(str).toString('base64')
}
function from_base64(base64) {
return new Buffer(base64, 'base64').toString()
}
// 加权随机
function load_balance(items) {
let list = []
for (let item of items) {
for (let i = 0; i < item.weight; i++) {
list.push(item.app_id)
}
}
let randint = getRandomInt(0, list.length)
return list[randint]
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
module.exports = {
test_order_id,
test_month,
which_month,
to_base64,
from_base64,
load_balance
}
\ No newline at end of file
const rp = require('request-promise'),
moment = require('moment'),
fs = require('fs'),
rsa = require('./rsa'),
orm = require('./orm'),
extend = require('extend'),
config = require('config'),
log = console.log.bind(console),
error = console.error.bind(console)
// const api_host = "https://zmopenapi.zmxy.com.cn/openapi.do" // 芝麻信用网关地址
// const private_key_file = "d:\\keys\\private_key.pem" // 商户私钥文件路径
// const zmPublic_key_file = "d:\\keys\\public_key.pem" // 芝麻公钥文件路径
// const app_id = "1000***" // 芝麻分配给商户的 appId
// params= URLEncode(Base64(RSAEncrypt(param1=URLEncode(param1value)&param2=URLEncode(param2value))))
class ZmopClient {
constructor() {
this.zmop_host = 'https://zmopenapi.zmxy.com.cn/openapi.do'
this.data = {
app_id: config.get("zmop_app_id"),
charset: 'UTF-8',
version: '1.0',
platform: 'zmop',
}
}
prepare(method, params) {
this.data.method = method
let extended = {
method: method,
params: rsa.RSAEncrypt(params),
sign: rsa.sign(params)
}
return extend(extended, this.data)
}
build_url(certNo, name, extras) {
try {
log(certNo)
log(extras)
let params = {
identity_type: 2,
identity_param: {"certNo": certNo.toString().toUpperCase(), "name": name, "certType": "IDENTITY_CARD"},
biz_params: {"auth_code": "M_H5", "channelType": "app", "state": extras}
}
let method = 'zhima.auth.info.authorize'
return this.zmop_host + '?' + rsa.params2str(this.prepare(method, params))
} catch (e) {
error(e)
throw e
}
}
// 查看是否授权
async authed(certNo, name) {
try {
let method = 'zhima.auth.info.authquery'
// let params = {
// auth_category: 'C2B',
// identity_type: '0',
// identity_param: {"openId": "268801234567890123456"}
// }
//使用身份证和姓名
let params = {
auth_category: 'C2B',
identity_type: '2',
identity_param: {"certNo": certNo.toString().toUpperCase(), "name": name, "certType": "IDENTITY_CARD"}
}
let resp = await rp.post({
url: this.zmop_host,
json: true,
form: this.prepare(method, params)
})
if (resp.encrypted) {
let decrypted_res = rsa.RSADecrypt(resp.biz_response)
if (rsa.verify(resp.biz_response_sign, decrypted_res)) {
return decrypted_res
} else {
throw new Error('api-authed 验签失败 resp')
}
} else {
return resp
}
} catch (e) {
throw e
}
}
async watch_list(open_id) {
let retry = 0
let self = this
async function get(transaction_id) {
try {
let method = 'zhima.credit.watchlistii.get'
let params = {
transaction_id: transaction_id,
product_code: 'w1010100100000000022',
open_id: open_id
}
let resp = await rp.post({
url: self.zmop_host,
json: true,
form: self.prepare(method, params)
})
let row = {
transaction_id: transaction_id,
form_data: JSON.stringify(resp),
open_id: open_id,
method: 'watch_list'
}
log('zmop_score resp: ', resp)
await orm.create_api_task(row)
if (resp.encrypted) {
let decrypted_res = rsa.RSADecrypt(resp.biz_response)
if (rsa.verify(resp.biz_response_sign, decrypted_res)) {
return JSON.parse(decrypted_res)
} else {
error('api-score 验签失败: ', decrypted_res)
throw new Error('api-score 验签失败')
}
} else {
let biz_response = JSON.parse(resp.biz_response)
//{ encrypted: false, biz_response: '{"success":false,"error_code":"ZMCSP.zm_account_not_existed","error_message":"芝麻账户不存在"}' }
if (biz_response.error_code == 'ZMCREDIT.transaction_id_repeat' && retry < 3) {
retry++
let transaction_id = ZmopClient.build_transaction_id()
return await get(open_id, transaction_id)
} else {
return biz_response
}
}
} catch (e) {
throw e
}
}
let transaction_id = ZmopClient.build_transaction_id()
return await get(transaction_id)
}
// 查询芝麻分接口
async score(open_id) {
let retry = 0
let self = this
async function get(transaction_id) {
try {
let method = 'zhima.credit.score.get'
let params = {
transaction_id: transaction_id,
product_code: 'w1010100100000000001',
open_id: open_id
}
let resp = await rp.post({
url: self.zmop_host,
json: true,
form: self.prepare(method, params)
})
let row = {
transaction_id: transaction_id,
form_data: JSON.stringify(resp),
open_id: open_id,
method: 'score'
}
log('zmop_score resp: ', resp)
await orm.create_api_task(row)
if (resp.encrypted) {
let decrypted_res = rsa.RSADecrypt(resp.biz_response)
if (rsa.verify(resp.biz_response_sign, decrypted_res)) {
return JSON.parse(decrypted_res)
} else {
error('api-score 验签失败: ', decrypted_res)
throw new Error('api-score 验签失败')
}
} else {
let biz_response = JSON.parse(resp.biz_response)
//{ encrypted: false, biz_response: '{"success":false,"error_code":"ZMCSP.zm_account_not_existed","error_message":"芝麻账户不存在"}' }
if (biz_response.error_code == 'ZMCREDIT.transaction_id_repeat' && retry < 3) {
retry++
let transaction_id = ZmopClient.build_transaction_id()
return await get(open_id, transaction_id)
} else {
return biz_response
}
}
} catch (e) {
throw e
}
}
let transaction_id = ZmopClient.build_transaction_id()
return await get(transaction_id)
}
static
build_transaction_id() {
let t = moment().format('YYYYMMDDHHmmssSSS')
let str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
while (t.length < 32) {
t += str[Math.round(Math.random() * (str.length - 1))]
}
return t
}
}
// let t = ZmopClient.build_transaction_id()
// let open_id = '268806822853700342467205358'
// let zmop = new ZmopClient()
// console.log(zmop.build_url('330182199401213216', '黄炜'))
module.exports = ZmopClient
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCir0A1PyJ+v56i21m/hjECVtM43oq7Uj8SC1lDhJqeOWMCZEQc
ChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5GcyX68VPBTeRml7wtp/0oYoW1c
9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+T4R2LdwbaK2hUnKdFwIDAQAB
AoGAJlbw5duUMJTOOQlC1G02nZuCufNQdKcEcCc2iT/2BHX3zHd+ybDh1dIcuhhU
2cx231W+CGi60ikljH1gXcWu0GAbGMSyvC4sTs+sDNpzp2peYtZ67bSNp7rd6sNA
v8zZgC66zpNQVuD0WQNxHAISQNUNNaxDGz+AYRIBM4VKy7kCQQDOnWsEHKdr12f5
1GgiownfoC53U+oRiyuoxFx+YS7iR9QCAPRHx8822LgThMA1qvINKfDwtocpK+UO
imi2MyHNAkEAyZHGRPO+ij9XRIw5IfFNpGpt40MQv4PQYpmI/of7zbBVUaEFvJ0y
DZ6s9KhU7cHblKsruUHu6KZS/Lu37k4mcwJAQNf2thuoD5hS8X1QEU7J0n0bGCgl
1cuvsA+wV5l4dVvFlbtm7wAucDAj1Txcdntn6+m1zFX8pVc5VA7sPgJeIQJAG1SO
o47BCRGpjXvNy1JiiqZ7m9LeFHmU6amTr59UfwEnsFe65WYTAVHAdbPVQB+G3FOR
LB9Ke3UBz47MU515DQJAMeAIdlp71W1TKNhLv5pLQtfSmFzMQWvhXZuLnysvqFfg
IJpsio6hJcr9sJI72s6kBPaVyVvwCYOlreXq2SM2uw==
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyDAB5cXvXum9x9z3S6akLTxnZ
curc0hpldpWdlB315gOUbKvjJKvpaocWiVMHMcv6UZ2Ml7TVXXmEd/7e7iG1wIJm
ocNaC86wpOyLqlsg7P22weuy3dq8SyOcZ8PQkn346VahJ7OUoPNc/8Y23tNJXdO/
+lpl98nzOe0ABdanIQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCir0A1PyJ+v56i21m/hjECVtM43oq7Uj8SC1lDhJqeOWMCZEQc
ChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5GcyX68VPBTeRml7wtp/0oYoW1c
9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+T4R2LdwbaK2hUnKdFwIDAQAB
AoGAJlbw5duUMJTOOQlC1G02nZuCufNQdKcEcCc2iT/2BHX3zHd+ybDh1dIcuhhU
2cx231W+CGi60ikljH1gXcWu0GAbGMSyvC4sTs+sDNpzp2peYtZ67bSNp7rd6sNA
v8zZgC66zpNQVuD0WQNxHAISQNUNNaxDGz+AYRIBM4VKy7kCQQDOnWsEHKdr12f5
1GgiownfoC53U+oRiyuoxFx+YS7iR9QCAPRHx8822LgThMA1qvINKfDwtocpK+UO
imi2MyHNAkEAyZHGRPO+ij9XRIw5IfFNpGpt40MQv4PQYpmI/of7zbBVUaEFvJ0y
DZ6s9KhU7cHblKsruUHu6KZS/Lu37k4mcwJAQNf2thuoD5hS8X1QEU7J0n0bGCgl
1cuvsA+wV5l4dVvFlbtm7wAucDAj1Txcdntn6+m1zFX8pVc5VA7sPgJeIQJAG1SO
o47BCRGpjXvNy1JiiqZ7m9LeFHmU6amTr59UfwEnsFe65WYTAVHAdbPVQB+G3FOR
LB9Ke3UBz47MU515DQJAMeAIdlp71W1TKNhLv5pLQtfSmFzMQWvhXZuLnysvqFfg
IJpsio6hJcr9sJI72s6kBPaVyVvwCYOlreXq2SM2uw==
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCir0A1PyJ+v56i21m/hjECVtM4
3oq7Uj8SC1lDhJqeOWMCZEQcChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5Gc
yX68VPBTeRml7wtp/0oYoW1c9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+
T4R2LdwbaK2hUnKdFwIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyDAB5cXvXum9x9z3S6akLTxnZ
curc0hpldpWdlB315gOUbKvjJKvpaocWiVMHMcv6UZ2Ml7TVXXmEd/7e7iG1wIJm
ocNaC86wpOyLqlsg7P22weuy3dq8SyOcZ8PQkn346VahJ7OUoPNc/8Y23tNJXdO/
+lpl98nzOe0ABdanIQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyDAB5cXvXum9x9z3S6akLTxnZ
curc0hpldpWdlB315gOUbKvjJKvpaocWiVMHMcv6UZ2Ml7TVXXmEd/7e7iG1wIJm
ocNaC86wpOyLqlsg7P22weuy3dq8SyOcZ8PQkn346VahJ7OUoPNc/8Y23tNJXdO/
+lpl98nzOe0ABdanIQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCir0A1PyJ+v56i21m/hjECVtM43oq7Uj8SC1lDhJqeOWMCZEQc
ChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5GcyX68VPBTeRml7wtp/0oYoW1c
9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+T4R2LdwbaK2hUnKdFwIDAQAB
AoGAJlbw5duUMJTOOQlC1G02nZuCufNQdKcEcCc2iT/2BHX3zHd+ybDh1dIcuhhU
2cx231W+CGi60ikljH1gXcWu0GAbGMSyvC4sTs+sDNpzp2peYtZ67bSNp7rd6sNA
v8zZgC66zpNQVuD0WQNxHAISQNUNNaxDGz+AYRIBM4VKy7kCQQDOnWsEHKdr12f5
1GgiownfoC53U+oRiyuoxFx+YS7iR9QCAPRHx8822LgThMA1qvINKfDwtocpK+UO
imi2MyHNAkEAyZHGRPO+ij9XRIw5IfFNpGpt40MQv4PQYpmI/of7zbBVUaEFvJ0y
DZ6s9KhU7cHblKsruUHu6KZS/Lu37k4mcwJAQNf2thuoD5hS8X1QEU7J0n0bGCgl
1cuvsA+wV5l4dVvFlbtm7wAucDAj1Txcdntn6+m1zFX8pVc5VA7sPgJeIQJAG1SO
o47BCRGpjXvNy1JiiqZ7m9LeFHmU6amTr59UfwEnsFe65WYTAVHAdbPVQB+G3FOR
LB9Ke3UBz47MU515DQJAMeAIdlp71W1TKNhLv5pLQtfSmFzMQWvhXZuLnysvqFfg
IJpsio6hJcr9sJI72s6kBPaVyVvwCYOlreXq2SM2uw==
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCir0A1PyJ+v56i21m/hjECVtM4
3oq7Uj8SC1lDhJqeOWMCZEQcChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5Gc
yX68VPBTeRml7wtp/0oYoW1c9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+
T4R2LdwbaK2hUnKdFwIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyM+GAgdFQebzSWf7laNMxwdR6
SzNW8pouEIyODY4VoEaM2YE3Lye75jBNxHT5+4GCI0SGyLG/hvaZSfYkLWjEpai+
3UJ914q9rFtLpoiRDYwxBpEhC+1fQrE2D3jRJi1ON/VOQ6nUU8sDTKBYSqBXzUBJ
qLgvyPSNC6xJjIBWZQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCir0A1PyJ+v56i21m/hjECVtM43oq7Uj8SC1lDhJqeOWMCZEQc
ChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5GcyX68VPBTeRml7wtp/0oYoW1c
9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+T4R2LdwbaK2hUnKdFwIDAQAB
AoGAJlbw5duUMJTOOQlC1G02nZuCufNQdKcEcCc2iT/2BHX3zHd+ybDh1dIcuhhU
2cx231W+CGi60ikljH1gXcWu0GAbGMSyvC4sTs+sDNpzp2peYtZ67bSNp7rd6sNA
v8zZgC66zpNQVuD0WQNxHAISQNUNNaxDGz+AYRIBM4VKy7kCQQDOnWsEHKdr12f5
1GgiownfoC53U+oRiyuoxFx+YS7iR9QCAPRHx8822LgThMA1qvINKfDwtocpK+UO
imi2MyHNAkEAyZHGRPO+ij9XRIw5IfFNpGpt40MQv4PQYpmI/of7zbBVUaEFvJ0y
DZ6s9KhU7cHblKsruUHu6KZS/Lu37k4mcwJAQNf2thuoD5hS8X1QEU7J0n0bGCgl
1cuvsA+wV5l4dVvFlbtm7wAucDAj1Txcdntn6+m1zFX8pVc5VA7sPgJeIQJAG1SO
o47BCRGpjXvNy1JiiqZ7m9LeFHmU6amTr59UfwEnsFe65WYTAVHAdbPVQB+G3FOR
LB9Ke3UBz47MU515DQJAMeAIdlp71W1TKNhLv5pLQtfSmFzMQWvhXZuLnysvqFfg
IJpsio6hJcr9sJI72s6kBPaVyVvwCYOlreXq2SM2uw==
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCir0A1PyJ+v56i21m/hjECVtM4
3oq7Uj8SC1lDhJqeOWMCZEQcChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5Gc
yX68VPBTeRml7wtp/0oYoW1c9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+
T4R2LdwbaK2hUnKdFwIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMAfFrTSkWbgM4/8At4ACDCcEn
ttXsvO/LgGVeYo+8V7RkLZCJBvOFAAXTNTFgFlUY9MhsElsgIqegmapt0K+DGvLn
kc2+0LPLI0FFeLMdKfHbCJk7U7HO2iKST0nqUSTcm9izWHUDRDfynVjrFO7w0FXB
Oousa/xcIXupmGzPfQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCir0A1PyJ+v56i21m/hjECVtM43oq7Uj8SC1lDhJqeOWMCZEQc
ChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5GcyX68VPBTeRml7wtp/0oYoW1c
9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+T4R2LdwbaK2hUnKdFwIDAQAB
AoGAJlbw5duUMJTOOQlC1G02nZuCufNQdKcEcCc2iT/2BHX3zHd+ybDh1dIcuhhU
2cx231W+CGi60ikljH1gXcWu0GAbGMSyvC4sTs+sDNpzp2peYtZ67bSNp7rd6sNA
v8zZgC66zpNQVuD0WQNxHAISQNUNNaxDGz+AYRIBM4VKy7kCQQDOnWsEHKdr12f5
1GgiownfoC53U+oRiyuoxFx+YS7iR9QCAPRHx8822LgThMA1qvINKfDwtocpK+UO
imi2MyHNAkEAyZHGRPO+ij9XRIw5IfFNpGpt40MQv4PQYpmI/of7zbBVUaEFvJ0y
DZ6s9KhU7cHblKsruUHu6KZS/Lu37k4mcwJAQNf2thuoD5hS8X1QEU7J0n0bGCgl
1cuvsA+wV5l4dVvFlbtm7wAucDAj1Txcdntn6+m1zFX8pVc5VA7sPgJeIQJAG1SO
o47BCRGpjXvNy1JiiqZ7m9LeFHmU6amTr59UfwEnsFe65WYTAVHAdbPVQB+G3FOR
LB9Ke3UBz47MU515DQJAMeAIdlp71W1TKNhLv5pLQtfSmFzMQWvhXZuLnysvqFfg
IJpsio6hJcr9sJI72s6kBPaVyVvwCYOlreXq2SM2uw==
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCir0A1PyJ+v56i21m/hjECVtM4
3oq7Uj8SC1lDhJqeOWMCZEQcChHYpnRkhyrypVRhNKXq7edq5Cp1f5/q4mPrw5Gc
yX68VPBTeRml7wtp/0oYoW1c9wEqbgPWsHelVdb+2VN3V6Hryot7ovz01jkndPK+
T4R2LdwbaK2hUnKdFwIDAQAB
-----END PUBLIC KEY-----
tt.51gjj.com:5000/redirect?params=JevbQqScHiM1Qoryf1%2FD4MIrl7mvWRtt8neRUzLelEdru%2FRUt8N%2FQOs3qWgdOqUXVNXZnUL87Bc0uFFm4dxAwRRNxr0ek4kFPBoDwuk063AS3vFz%2Bsm0wy808hX6s63MnXfCbGqq1tpvkVb4mFp1jDFXJJgZjOoJ%2FBhTLfQWI%2BSE4YjWcvm3%2BV9enCv8TrYZ3jPhmf%2F3KeX%2BuZB3HhWmnqQHv6nu9Wbh%2Fqf2sevtvWzV08F6eT8JcDhF%2FK%2F1aZvSVgr83%2BuzRbO%2BCUa7KY0GWNc5moFfhT45TeR2PslcTDde36sUumlLdEQxR2qXXf7TPL2z0Y0bWKE7JMpbx%2BqnPg%3D%3D&sign=HOg8UcgLT4zFTq5J7sTy9N7XHjS%2Fg8NfMnYoR4ip9UdPPScHXiqnuIDZ44zQT9NU5X6z3XoeGt04YFfeDS3DXGaD1YhyTvWwCigAMPnu7YlSG1ZeAputY0xD8sCkN7Dv20GQE42%2BU%2BSOQk8wzcsHJ87KwQUkXF9oTQviaIS0fk0%3D
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyDAB5cXvXum9x9z3S6akLTxnZ
curc0hpldpWdlB315gOUbKvjJKvpaocWiVMHMcv6UZ2Ml7TVXXmEd/7e7iG1wIJm
ocNaC86wpOyLqlsg7P22weuy3dq8SyOcZ8PQkn346VahJ7OUoPNc/8Y23tNJXdO/
+lpl98nzOe0ABdanIQIDAQAB
-----END PUBLIC KEY-----
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
const NodeRSA = require('node-rsa');
// def sign(privatekey, data):
// '''
// 加签
// :param privatekey:私钥文件路径
// :param data:要加签的数据
// :return string:签名
// '''
// signEVP = EVP.load_key(privatekey)
// signEVP.sign_init()
// signEVP.sign_update(data)
// result = signEVP.sign_final()
// return base64.b64encode(result)
// 加载密钥文件
const private_key = fs.readFileSync(path.join(__dirname, 'keys/rsa_private_key.pem')).toString()
let zmop_public_key = {}
let file_list = fs.readdirSync(path.join(__dirname, 'keys'))
for (let dir of file_list) {
let f = fs.lstatSync(path.join(__dirname, 'keys/' + dir))
if (f.isDirectory()) {
zmop_public_key[dir] = fs.readFileSync(path.join(__dirname, `keys/${dir}/zmop_public_key.pem`)).toString()
}
}
//params= URLEncode(Base64(RSAEncrypt(param1=URLEncode(param1value)&param2=URLEncode(param2value))))
// 加签
// :param privatekey:私钥文件路径
// :param data:要加签的数据
// :return string:签名
function RSAEncrypt(app_id, data) {
let str = params2str(data)
console.log('app_id: ', app_id)
var key = new NodeRSA(zmop_public_key[app_id])
key.setOptions({encryptionScheme: 'pkcs1'});
let encrypted = key.encrypt(str, 'base64')
return encrypted
}
function RSADecrypt(str) {
let key = new NodeRSA(private_key)
key.setOptions({encryptionScheme: 'pkcs1'});
let encrypted = key.decrypt(str, 'utf8')
return encrypted
}
// 转换业务参数为str
function params2str(data) {
let str = ''
for (let k of Object.keys(data)) {
let v = data[k]
if (Object.prototype.toString.call(v) === '[object Object]') {
v = JSON.stringify(v)
}
str += k + '=' + encodeURIComponent(v) + '&'
}
return str.slice(0, str.length - 1)
}
// str 转为 json
function str2params(str) {
let ret = {}
let arr = str.split('&')
for (let item of arr) {
let index = item.indexOf('=')
ret[item.slice(0, index)] = decodeURIComponent(item.slice(index + 1))
}
return ret
}
// 加签
function sign(data) {
// 签名采用sha1withRsa算法
let str = params2str(data)
let sign = crypto.createSign('RSA-SHA1')
sign.update(str)
return sign.sign(private_key, 'base64')
}
// 验签
// sign_value: 签名,
// str: 待验签的数据
function verify(sign_value, str, app_id) {
var verify = crypto.createVerify('RSA-SHA1');
verify.update(str);
return verify.verify(zmop_public_key[app_id], sign_value, 'base64')
}
// let resp = {
// "encrypted": true,
// "sign": {
// "signSource": "zhima_sign_value",
// "signResult": "OKICqUJzHMn9ee5p0xIlJE7L5FZ5nOmjtdDYCqHD3FsQCM5RLJ2U3kFCpXf4sWnSW5J/DRsCmYRffofZapP7wH//xrJq5n9rVDg+CHQ3i5E5Mrtv4/X3wsURaDXpX1wJr+mGd2RL8zTwDi9FW1N7bHK28I3sNgwaoWLeS15iJ4s="
// },
// "biz_response_sign": "cEHP5udepFpo6Ow/ejWwLR0Q1MPaxUZA17zt7Ezd73DVNUl8RqDf/mNzap79aOucOc53n0GXwY1HIQMLLNKI9w2va62SajFhrLFYMy+QtWN3w6EsnuWNOz0xQLKpKCw3HbloD2S6nYk1dg3fBWMhC2pyZ8xXES4l6nZXWzTsOnc=",
// "biz_response": "gG4WYaHXetiko/9nqvGPGDVIyY055WgIDQ3RxVEmX9xVRAO9m8rzO33G0lO8S3iQkfHnkjZhbiQhWP7Fm8x9LtEwm8GjnSl+6egPgORUazOAdKdbvRWr9ri18znCJLu5JBr9QH6J7NoPU5YV231fN4bCPRnpD603p96TPQ/Zw/k="
// }
// if (resp.encrypted) {
// let decrypted_res = RSADecrypt(resp.biz_response)
// console.log(JSON.parse(decrypted_res))
// if (verify(resp.biz_response_sign, decrypted_res)) {
// console.log('success')
// } else {
// console.log('error')
// }
// }
module.exports = {
sign,
verify,
RSAEncrypt,
RSADecrypt,
params2str,
str2params
}
const rp = require('request-promise'),
moment = require('moment'),
fs = require('fs'),
rsa = require('./rsa'),
orm = require('../orm'),
extend = require('extend'),
config = require('config'),
log = console.log.bind(console),
error = console.error.bind(console)
// const api_host = "https://zmopenapi.zmxy.com.cn/openapi.do" // 芝麻信用网关地址
// const private_key_file = "d:\\keys\\private_key.pem" // 商户私钥文件路径
// const zmPublic_key_file = "d:\\keys\\public_key.pem" // 芝麻公钥文件路径
// const app_id = "1000***" // 芝麻分配给商户的 appId
// params= URLEncode(Base64(RSAEncrypt(param1=URLEncode(param1value)&param2=URLEncode(param2value))))
class ZmopClient {
constructor() {
this.zmop_host = 'https://zmopenapi.zmxy.com.cn/openapi.do'
this.data = {
charset: 'UTF-8',
version: '1.0',
platform: 'zmop',
}
}
prepare(app_id, method, params) {
this.data.app_id = app_id,
this.data.method = method
let extended = {
params: rsa.RSAEncrypt(app_id, params),
sign: rsa.sign(params)
}
return extend(extended, this.data)
}
build_url(app_id, certNo, name, extras) {
try {
let params = {
identity_type: 2,
identity_param: {"certNo": certNo.toString().toUpperCase(), "name": name, "certType": "IDENTITY_CARD"},
biz_params: {"auth_code": "M_H5", "channelType": "app", "state": extras}
}
let method = 'zhima.auth.info.authorize'
return this.zmop_host + '?' + rsa.params2str(this.prepare(app_id, method, params))
} catch (e) {
error(e)
throw e
}
}
// 查看是否授权
async authed(app_id, certNo, name) {
try {
let method = 'zhima.auth.info.authquery'
// let params = {
// auth_category: 'C2B',
// identity_type: '0',
// identity_param: {"openId": "268801234567890123456"}
// }
//使用身份证和姓名
let params = {
auth_category: 'C2B',
identity_type: '2',
identity_param: {"certNo": certNo.toString().toUpperCase(), "name": name, "certType": "IDENTITY_CARD"}
}
let resp = await rp.post({
url: this.zmop_host,
json: true,
form: this.prepare(app_id, method, params)
})
if (resp.encrypted) {
let decrypted_res = rsa.RSADecrypt(resp.biz_response)
if (rsa.verify(resp.biz_response_sign, decrypted_res, app_id)) {
return decrypted_res
} else {
throw new Error('api-authed 验签失败 resp')
}
} else {
return resp
}
} catch (e) {
throw e
}
}
async watch_list(app_id, open_id) {
let retry = 0
let self = this
async function get(transaction_id) {
try {
let method = 'zhima.credit.watchlistii.get'
let params = {
transaction_id: transaction_id,
product_code: 'w1010100100000000022',
open_id: open_id
}
let resp = await rp.post({
url: self.zmop_host,
json: true,
form: self.prepare(app_id, method, params)
})
let row = {
transaction_id: transaction_id,
resp: JSON.stringify(resp),
open_id: open_id,
platform: 'zhima',
method: 'watch_list'
}
log('zmop_score resp: ', resp)
await orm.create_api_task(row)
if (resp.encrypted) {
let decrypted_res = rsa.RSADecrypt(resp.biz_response)
if (rsa.verify(resp.biz_response_sign, decrypted_res, app_id)) {
return JSON.parse(decrypted_res)
} else {
error('api-score 验签失败: ', decrypted_res)
throw new Error('api-score 验签失败')
}
} else {
let biz_response = JSON.parse(resp.biz_response)
//{ encrypted: false, biz_response: '{"success":false,"error_code":"ZMCSP.zm_account_not_existed","error_message":"芝麻账户不存在"}' }
if (biz_response.error_code == 'ZMCREDIT.transaction_id_repeat' && retry < 3) {
retry++
let transaction_id = ZmopClient.build_transaction_id()
return await get(open_id, transaction_id)
} else {
return biz_response
}
}
} catch (e) {
throw e
}
}
let transaction_id = ZmopClient.build_transaction_id()
return await get(transaction_id)
}
// 查询芝麻分接口
async score(app_id, open_id) {
let retry = 0
let self = this
async function get(transaction_id) {
try {
let method = 'zhima.credit.score.get'
let params = {
transaction_id: transaction_id,
product_code: 'w1010100100000000001',
open_id: open_id
}
let resp = await rp.post({
url: self.zmop_host,
json: true,
form: self.prepare(app_id, method, params)
})
let row = {
transaction_id: transaction_id,
resp: JSON.stringify(resp),
open_id: open_id,
platform: 'zhima',
method: 'score'
}
log('zmop_score resp: ', resp)
await orm.create_api_task(row)
if (resp.encrypted) {
let decrypted_res = rsa.RSADecrypt(resp.biz_response)
if (rsa.verify(resp.biz_response_sign, decrypted_res, app_id)) {
return JSON.parse(decrypted_res)
} else {
error('api-score 验签失败: ', decrypted_res)
throw new Error('api-score 验签失败')
}
} else {
let biz_response = JSON.parse(resp.biz_response)
//{ encrypted: false, biz_response: '{"success":false,"error_code":"ZMCSP.zm_account_not_existed","error_message":"芝麻账户不存在"}' }
if (biz_response.error_code == 'ZMCREDIT.transaction_id_repeat' && retry < 3) {
retry++
let transaction_id = ZmopClient.build_transaction_id()
return await get(open_id, transaction_id)
} else {
return biz_response
}
}
} catch (e) {
throw e
}
}
let transaction_id = ZmopClient.build_transaction_id()
return await get(transaction_id)
}
static
build_transaction_id() {
let t = moment().format('YYYYMMDDHHmmssSSS')
let str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
while (t.length < 32) {
t += str[Math.round(Math.random() * (str.length - 1))]
}
return t
}
}
// let t = ZmopClient.build_transaction_id()
// let open_id = '268806822853700342467205358'
// let zmop = new ZmopClient()
// setTimeout(async () => {
// console.log(await zmop.authed('421224199107090029', '吴莹'))
// })
module.exports = ZmopClient
/**
* Created by xaj on 2017/1/11.
*/
"use strict";
var config = require('config'),
log4js = require('log4js'),
log4jsConfig = require('./logger'),
basic = require('./basic');
module.exports = {
"config" : config,
"log4js" : log4js,
"log4jsConfig" : log4jsConfig,
"basic" : basic
};
\ No newline at end of file
var jwt = require('jsonwebtoken'),
moment = require('moment'),
extend = require('extend'),
fs = require('fs');
module.exports = function (options) {
var opts = extend({}, {
pub_cert_file: './rsa_public_key.pem',
pri_cert_file: './rsa_private_key.pem',
query_name: 'token',
data_name: 'phone',
//指定过期时间
expiresIn: moment().add(1, 'years').unix(),
//用于从请求信息中获取登录凭证,用户名、密码...
getCredentials: function () {
return {};
},
//用于登录验证,根据用户名密码验证用户是否有效
verifyIdentity: function () {
return true;
}
}, options);
//公钥
var pub_cert = fs.readFileSync(opts.pub_cert_file);
//私钥
var pri_cert = fs.readFileSync(opts.pri_cert_file);
return {
_create: function (data) {
var payload = {};
payload[opts.data_name] = data;
return jwt.sign(payload, pri_cert, {
expiresIn: opts.expiresIn,
algorithm: 'RS256',
noTimestamp: true
});
},
//第一次生成token
generateToken: function (req) {
var data = opts.verifyIdentity(opts.getCredentials(req));
if (!data) return '';
return this._create(data);
},
// 销毁token, 过期时间改为当前时间
destory: function () {
var payload = this.verify(req);
opts.expiresIn = moment().unix()
if (payload) return this._create(payload[opts.data_name]);
return '';
},
//刷新token
refreshToken: function (req) {
var payload = this.verify(req);
if (payload) return this._create(payload[opts.data_name]);
return '';
},
//从请求中获取token的字符串
_getReqToken: function (req) {
var token = '';
//获取token
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
token = req.headers.authorization.split(' ')[1];
} else if (req.query && req.query[opts.query_name]) {
token = req.query[opts.query_name];
}
return token;
},
//验证token并得到其中的payload
verify: function (req) {
var token = this._getReqToken(req);
if (!token) return false;
var payload = null;
try {
//验证token是否有效
payload = jwt.verify(token, pub_cert, {algorithms: 'RS256'});
} catch (err) {
console.log(err);
}
return payload;
},
//token解码
decode: function (token) {
var decoded = null;
try {
decoded = jwt.decode(token, {complete: true, json: true});
} catch (err) {
console.log(err);
}
return decoded;
},
//获得身份信息
getIdentity: function (req) {
var payload = this.verify(req);
return payload && payload[opts.data_name];
}
}
};
\ No newline at end of file
/**
* Created by xaj on 2017/1/11.
*
* Description: common methods such as get local ip, make muti-level directory, string format, and so on;
* common data&check methods which need to disclosure from constants.js.
*
*/
"use strict";
var path = require('path'),
os = require('os'),
fs = require('fs');
module.exports = {
/*
* iftype: 网卡类型 eth, lo, tunl
* family: IPv4 or IPv6
* default return: '127.0.0.1'
*
* Description: get ipv4 or ipv6 address of assigned interface
* eg: getLocalIP(eth, IPv4)
* */
"getLocalIP" : function (iftype, family) {
var ip = '127.0.0.1';
var ifs = os.networkInterfaces();
for (var dev in ifs) {
if (dev.toLowerCase().match(iftype.toLowerCase())) {
var devinfo = ifs[dev];
for (var i=0; i<devinfo.length; i++) {
if (devinfo[i].family.toLowerCase() === family.toLowerCase()) {
ip = devinfo[i].address;
break;
}
}
break;
}
}
return ip;
},
/*
* dirpath: absolute directory path
*
* Description: asynchronous make directory
* */
"mkdirs" : function mkdirs(dirpath, mode, callback) {
fs.exists(dirpath, function(exists){
if (exists) {
callback(dirpath);
} else {
mkdirs(path.dirname(dirpath), mode, function(){
fs.mkdir(dirpath, mode, callback);
});
}
});
},
/*
* dirpath: absolute directory path
*
* Description: synchronous make directory
* */
"mkdirsSync" : function mkdirsSync(dirpath, mode) {
if (fs.existsSync(dirpath))
{
return;
} else {
mkdirsSync(path.dirname(dirpath), mode);
fs.mkdirSync(dirpath, mode);
}
},
/*
* s: string
*
* Description: string format, change multi spaces to one space
* eg: tmp = trim(stdout).split(' '), then tmp is an array obj
* */
"trimSpaces" : function (s) {
return s.replace(/(^\s*)|(\s*$)/g, '').replace(/\s+/g,' ');
},
};
var config = require('config');
var fs = require('fs');
var log4js = require('log4js');
var basic = require('./basic');
var log4jsConfig;
if (config.has('log4jsConfig')) {
log4jsConfig = config.get('log4jsConfig');
}
if(log4jsConfig) {
let name = config.get('log4jsName');
log4jsConfig.appenders[1].category.unshift(name);
/* log日志的配置 */
var logDir = log4jsConfig.appenders[1].filename;
if(logDir) {
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
}
/* 给日志文件名加上IP地址 */
var IpAdress = basic.getLocalIP('eth', 'IPv4');
if(log4jsConfig.appenders) {
for (var i = 0; i < log4jsConfig.appenders.length; i++) {
if (log4jsConfig.appenders[i].pattern) {
/* 把IP和微服务名移到filename字符串前面 */
var serviceName = log4jsConfig.appenders[i].pattern.split("-", 1);
log4jsConfig.appenders[i].pattern = log4jsConfig.appenders[i].pattern.slice(serviceName[0].length);
log4jsConfig.appenders[i].filename = log4jsConfig.appenders[i].filename + IpAdress + '-' + name + serviceName[0];
}
}
}
log4js.configure(log4jsConfig);
/* 将uncaughtException异常截获打印到log */
process.on('uncaughtException',function(err){
console.error(err.stack);
process.emit('exit',1);
});
}
module.exports = log4jsConfig;
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "zmop",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"lib": "lib"
},
"scripts": {
"start": "cross-env NODE_ENV=production&&node kqyx.js",
"dev": "supervisor index"
},
"author": "xaj",
"license": "MIT",
"dependencies": {
"ali-oss": "^5.2.0",
"body-parser": "^1.17.2",
"co": "^4.6.0",
"config": "^1.28.1",
"connect-multiparty": "^2.1.0",
"cookie-parser": "^1.4.3",
"crypto": "0.0.3",
"express": "^4.16.2",
"express-ws": "^3.0.0",
"extend": "^3.0.1",
"hbs": "^4.0.1",
"https": "^1.0.0",
"iconv-lite": "^0.4.17",
"log4js": "0.6.27",
"moment": "^2.18.1",
"mysql": "^2.15.0",
"mysql2": "^1.5.3",
"node-rsa": "^0.4.2",
"node-schedule": "^1.2.3",
"nunjucks": "^3.1.2",
"request": "^2.83.0",
"request-promise": "^4.2.2",
"sequelize": "^4.37.6"
},
"devDependencies": {
"cross-env": "^5.1.4"
}
}
This diff is collapsed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>芝麻分接口文档</title>
<meta name="description" content="Description">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="vendor/themes/vue.css">
<style>
.markdown-section {
margin-left: 20px;
}
.field-support {
background: content-box;
font-size: 15px;
border: 1px solid #ddd;
padding: 6px 20px;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script>
window.$docsify = {
name: '芝麻分接口文档',
repo: '', themeColor: '#3F51B5'
}
</script>
<script src="vendor/docsify.js"></script>
<script src="vendor/themes/bash.js"></script>
<script src="vendor/themes/json_base.js"></script>
</html>
const express = require('express');
const http = require('http');
const path = require('path');
const config = require('config');
// middlewares
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const timeInterval = config.get('timeInterval');
const scheduleproc = require('./lib/scheduleproc');
var app = express();
var login = require('./routers/login');
var pay = require('./routers/pay');
var tools = require('./routers/tools');
var main = require('./routers/main');
var data_service = require('./routers/data_service');
//X-Frame-Options: Allow-From http://www.growingio.com
app.use('*', function (req, res, next) {
res.setHeader('X-Frame-Options', 'ALLOW-FROM https://www.growingio.com');
next();
})
// 给app配置bodyParser中间件
// 通过如下配置再路由种处理request时,可以直接获得post请求的body部分
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
const aes = require('./lib/aes');
const token = require('./lib/token')
app.use(function (req, res, next) {
if (req.body.data) {
try {
req.body = aes.aesDecrypt(req.body.data)
if (!req.body) {
return res.send({code: 199, msg: '系统错误'})
}
req.body = JSON.parse(req.body)
} catch (e) {
console.log(e)
return res.send({code: 199, msg: '系统错误'})
}
}
// if (!/login/.test(req.path)) {
// try {
// let test = token.decode(req.headers.authorization)
// if (test) {
// req.body.userId = test.userId
// }
// }catch(e) {
// return res.send({code: -1, msg: '无效的authorization token'})
// }
// }
console.log(req.path, ': ', req.body)
next()
})
// 注册路由
app.use('/login', login);
app.use('/tools', tools);
app.use('/data', main);
app.use('/pay', pay);
app.use('/dataService', data_service);
const httpPort = config.get('httpPort')
var Server = http.createServer(app);
Server.listen(httpPort, function () {
console.log((new Date()) + ' server start http server and listen on: ' + JSON.stringify(Server.address()));
});
// 定时清理日志任务
scheduleproc.BeginCleanLogWork(timeInterval);
\ No newline at end of file
This diff is collapsed.
(function(Prism) {
var insideString = {
variable: [
// Arithmetic Environment
{
pattern: /\$?\(\([\s\S]+?\)\)/,
inside: {
// If there is a $ sign at the beginning highlight $(( and )) as variable
variable: [{
pattern: /(^\$\(\([\s\S]+)\)\)/,
lookbehind: true
},
/^\$\(\(/,
],
number: /\b-?(?:0x[\dA-Fa-f]+|\d*\.?\d+(?:[Ee]-?\d+)?)\b/,
// Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic
operator: /--?|-=|\+\+?|\+=|!=?|~|\*\*?|\*=|\/=?|%=?|<<=?|>>=?|<=?|>=?|==?|&&?|&=|\^=?|\|\|?|\|=|\?|:/,
// If there is no $ sign at the beginning highlight (( and )) as punctuation
punctuation: /\(\(?|\)\)?|,|;/
}
},
// Command Substitution
{
pattern: /\$\([^)]+\)|`[^`]+`/,
inside: {
variable: /^\$\(|^`|\)$|`$/
}
},
/\$(?:[a-z0-9_#\?\*!@]+|\{[^}]+\})/i
],
};
Prism.languages.bash = {
'shebang': {
pattern: /^#!\s*\/bin\/bash|^#!\s*\/bin\/sh/,
alias: 'important'
},
'comment': {
pattern: /(^|[^"{\\])#.*/,
lookbehind: true
},
'string': [
//Support for Here-Documents https://en.wikipedia.org/wiki/Here_document
{
pattern: /((?:^|[^<])<<\s*)(?:"|')?(\w+?)(?:"|')?\s*\r?\n(?:[\s\S])*?\r?\n\2/g,
lookbehind: true,
greedy: true,
inside: insideString
},
{
pattern: /(["'])(?:\\\\|\\?[^\\])*?\1/g,
greedy: true,
inside: insideString
}
],
'variable': insideString.variable,
// Originally based on http://ss64.com/bash/
'function': {
pattern: /(^|\s|;|\||&)(?:alias|apropos|apt-get|aptitude|aspell|awk|basename|bash|bc|bg|builtin|bzip2|cal|cat|cd|cfdisk|chgrp|chmod|chown|chroot|chkconfig|cksum|clear|cmp|comm|command|cp|cron|crontab|csplit|cut|date|dc|dd|ddrescue|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|enable|env|ethtool|eval|exec|expand|expect|export|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|getopts|git|grep|groupadd|groupdel|groupmod|groups|gzip|hash|head|help|hg|history|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|jobs|join|kill|killall|less|link|ln|locate|logname|logout|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|make|man|mkdir|mkfifo|mkisofs|mknod|more|most|mount|mtools|mtr|mv|mmv|nano|netstat|nice|nl|nohup|notify-send|npm|nslookup|open|op|passwd|paste|pathchk|ping|pkill|popd|pr|printcap|printenv|printf|ps|pushd|pv|pwd|quota|quotacheck|quotactl|ram|rar|rcp|read|readarray|readonly|reboot|rename|renice|remsync|rev|rm|rmdir|rsync|screen|scp|sdiff|sed|seq|service|sftp|shift|shopt|shutdown|sleep|slocate|sort|source|split|ssh|stat|strace|su|sudo|sum|suspend|sync|tail|tar|tee|test|time|timeout|times|touch|top|traceroute|trap|tr|tsort|tty|type|ulimit|umask|umount|unalias|uname|unexpand|uniq|units|unrar|unshar|uptime|useradd|userdel|usermod|users|uuencode|uudecode|v|vdir|vi|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yes|zip)(?=$|\s|;|\||&)/,
lookbehind: true
},
'keyword': {
pattern: /(^|\s|;|\||&)(?:let|:|\.|if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)(?=$|\s|;|\||&)/,
lookbehind: true
},
'boolean': {
pattern: /(^|\s|;|\||&)(?:true|false)(?=$|\s|;|\||&)/,
lookbehind: true
},
'operator': /&&?|\|\|?|==?|!=?|<<<?|>>|<=?|>=?|=~/,
'punctuation': /\$?\(\(?|\)\)?|\.\.|[{}[\];]/
};
var inside = insideString.variable[1].inside;
inside['function'] = Prism.languages.bash['function'];
inside.keyword = Prism.languages.bash.keyword;
inside.boolean = Prism.languages.bash.boolean;
inside.operator = Prism.languages.bash.operator;
inside.punctuation = Prism.languages.bash.punctuation;
})(Prism);
Prism.languages.json = {
'property': /"(?:\\.|[^\\"])*"(?=\s*:)/ig,
'string': /"(?!:)(?:\\.|[^\\"])*"(?!:)/g,
'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?)\b/g,
'punctuation': /[{}[\]);,]/g,
'operator': /:/g,
'boolean': /\b(true|false)\b/gi,
'null': /\bnull\b/gi
};
Prism.languages.jsonp = Prism.languages.json;
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>芝麻分接口文档</title>
<meta name="description" content="Description">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="vendor/themes/vue.css">
<style>
.markdown-section {
margin-left: 20px;
}
.field-support {
background: content-box;
font-size: 15px;
border: 1px solid #ddd;
padding: 6px 20px;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script>
window.$docsify = {
name: '芝麻分接口文档',
repo: '', themeColor: '#3F51B5'
}
</script>
<script src="vendor/docsify.js"></script>
<script src="vendor/themes/bash.js"></script>
<script src="vendor/themes/json_base.js"></script>
</html>
const express = require('express');
const http = require('http');
const path = require('path');
const config = require('config');
// middlewares
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const timeInterval = config.get('timeInterval');
const scheduleproc = require('./lib/scheduleproc');
var app = express();
var login = require('./routers/login');
var pay = require('./routers/pay');
var tools = require('./routers/tools');
var main = require('./routers/main');
var data_service = require('./routers/data_service');
//X-Frame-Options: Allow-From http://www.growingio.com
app.use('*', function (req, res, next) {
res.setHeader('X-Frame-Options', 'ALLOW-FROM https://www.growingio.com');
next();
})
// 给app配置bodyParser中间件
// 通过如下配置再路由种处理request时,可以直接获得post请求的body部分
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
const aes = require('./lib/aes');
const token = require('./lib/token')
app.use(function (req, res, next) {
if (req.body.data) {
try {
req.body = aes.aesDecrypt(req.body.data)
if (!req.body) {
return res.send({code: 199, msg: '系统错误'})
}
req.body = JSON.parse(req.body)
} catch (e) {
console.log(e)
return res.send({code: 199, msg: '系统错误'})
}
}
// if (!/login/.test(req.path)) {
// try {
// let test = token.decode(req.headers.authorization)
// if (test) {
// req.body.userId = test.userId
// }
// }catch(e) {
// return res.send({code: -1, msg: '无效的authorization token'})
// }
// }
console.log(req.path, ': ', req.body)
next()
})
// 注册路由
app.use('/login', login);
app.use('/tools', tools);
app.use('/data', main);
app.use('/pay', pay);
app.use('/dataService', data_service);
const httpPort = config.get('httpPort')
var Server = http.createServer(app);
Server.listen(httpPort, function () {
console.log((new Date()) + ' server start http server and listen on: ' + JSON.stringify(Server.address()));
});
// 定时清理日志任务
scheduleproc.BeginCleanLogWork(timeInterval);
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment