Commit 90359d5a authored by 李尚科's avatar 李尚科

wexin pay

parent de7f088d
Pipeline #13534 passed with stage
in 1 minute 5 seconds
'use strict';
const Controller = require('egg').Controller;
const xml2js = require('xml2js').parseString;
class OrderController extends Controller {
/**
......@@ -27,6 +27,50 @@ class OrderController extends Controller {
ctx.success({ results });
}
//微信支付
async payOrder() {
const { ctx } = this;
const order_id = ctx.params.order_id;
const result = await ctx.service.credit.common.unifiedOrder('wexin', order_id);
ctx.success({ result });
}
async payNotice() {
const { ctx } = this;
let data = '';
ctx.req.setEncoding('utf8');
ctx.req.on('data', function (chunk) {
data += chunk;
});
const getxml = await new Promise(function (resolve) {
ctx.req.on('end', function () {
resolve(data);
});
});
const ret_obj = await ctx.helper.xmlTojson(getxml);
ctx.logger.info({ notice_params: ret_obj });
const ret = await ctx.service.credit.common.WexinNotify(ret_obj);
ctx.success({ ret_obj, ret });
return;
ctx.success("<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>");
}
async checkPay() {
const { ctx } = this;
const order_id = ctx.params.order_id;
const is_pay = ctx.service.credit.order.checkOrderPay(order_id);
let result = false;
if (is_pay) {
result = true;
}
ctx.success({ result });
}
}
......
'use strict';
const XML2JS = require('xml2js');
module.exports = {
// 获取 Token
......@@ -269,6 +269,22 @@ module.exports = {
return ip;
},
/**
* @author lisk
* @description 生成随机字串
* @param {*} length
*/
createNoncestr(length = 32) {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const clen = chars.length;
let str = '';
for (let i = 0; i < length; i++) {
str += chars.substr(parseInt(Math.random() * clen) - 1, 1);
}
return str;
},
JsonParse(jsonStr) {
try {
......@@ -297,5 +313,30 @@ module.exports = {
return 1;
},
//固定位数前补充 0 操作
PrefixInteger(num, length) {
return (Array(length).join('0') + num).slice(-length);
},
async xmlTojson(data) {
const ret_p = new Promise((resolve, reject) => {
XML2JS.parseString(data, (err, result) => {
if (err) {
this.ctx.failed(err);
reject();
} else {
const ret = result.xml;
resolve(ret);
}
});
});
let ret_json = {};
await ret_p.then((resolve, reject) => { ret_json = resolve });
return ret_json;
},
jsonToxml(data) {
const bulider = new XML2JS.Builder();
const xml_params = bulider.buildObject(data);
return xml_params;
}
};
......@@ -33,12 +33,36 @@ module.exports = app => {
allowNull: false,
},
time_interval_active: {
type: INTEGER,
allowNull: false,
type: STRING,
allowNull: true,
field: 'time_interval_active',
get() {
const time_interval_active = this.getDataValue('time_interval_active');
if (time_interval_active) {
try {
return JSON.parse(time_interval_active);
} catch (error) {
return [];
}
}
return [];
},
},
city_active: {
type: INTEGER,
allowNull: false,
type: STRING,
allowNull: true,
field: 'city_active',
get() {
const city_active = this.getDataValue('city_active');
if (city_active) {
try {
return JSON.parse(city_active);
} catch (error) {
return [];
}
}
return [];
},
},
// is_deleted: {
// type: INTEGER,
......
'use strict';
const moment = require('moment');
module.exports = app => {
const { STRING, INTEGER, DATE, DECIMAL, TEXT, ENUM } = app.Sequelize;
const CreditPay = app.prometheusModel.define('credit_pay', {
id: {
type: INTEGER,
primaryKey: true,
autoIncrement: true
},
order_id: {
type: INTEGER,
allowNull: false,
},
trade_no: {
type: STRING,
allowNull: false,
},
prepay_id: {
type: STRING,
allowNull: false,
},
transaction_id: {
type: STRING,
allowNull: true,
},
amount: {
type: DECIMAL,
allowNull: false,
},
description: {
type: STRING,
allowNull: true,
},
client_ip: {
type: STRING,
allowNull: true,
},
unifiedorder_result: {
type: STRING,
allowNull: false,
},
pay_result: {
type: STRING,
allowNull: true,
},
status: {
type: INTEGER,
allowNull: true,
},
created_at: {
type: DATE,
allowNull: true,
get() {
const date = this.getDataValue('created_at');
return date ? moment(date).format('YYYY-MM-DD HH:mm:ss') : undefined;
},
}
}, {
timestamps: false,
tableName: 'credit_pay',
});
CreditPay.one = async (data) => {
const attributes = data.attributes ? data.attributes : {};
const where = data.where ? data.where : {};
return await CreditPay.findOne({
attributes: attributes,
where: where,
});
}
CreditPay.all = async (data) => {
const attributes = data.attributes ? data.attributes : {};
const where = data.where ? data.where : {};
const order = data.order ? data.order : [];
return await CreditPay.findAll({
attributes: attributes,
where: where,
order,
});
}
CreditPay.list = async (data = {}) => {
const limit = data.limit ? Number(data.limit) : 10;
const page = data.page ? data.page : 1;
const order = data.order ? data.order : [];
const attributes = data.attributes ? data.attributes : {};
const where = data.where ? data.where : {};
const condition = {
offset: (page - 1) * limit,
limit,
where: where,
order: order,
attributes: attributes,
};
const { count, rows } = await CreditPay.findAndCountAll(condition);
return { page, count, rows };
}
CreditPay.add = async (data) => {
try {
//返回promise对象实力 instance
const res = await CreditPay.create(data);
//从promise 实例中中获得需要的id号,id 必须是自增长,而且必须主键,否则返回null
return res.id;
} catch (error) {
throw (error);
}
}
CreditPay.edit = async (data) => {
const where = data.where;
const params = data.params;
try {
const res = await CreditPay.update(params, { where: where })
return res;
} catch (error) {
throw (error);
}
}
return CreditPay;
};
\ No newline at end of file
......@@ -5,6 +5,8 @@ module.exports = app => {
router.get('third', '/home', 'credit.order.getMyCredit');
router.get('third', '/history/:type', 'credit.order.getRecord');
router.get('third', '/order/pay/:order_id', 'credit.order.payOrder');
router.post('third', '/order/pay_notice', 'credit.order.payNotice');
//我的信用-黑名单报告
router.get('third', '/blacklist/report/:report_id', 'credit.blacklist.getBlacklistReport');//获取报告信息
......
......@@ -36,7 +36,7 @@ class CallriskService extends Service {
const { ctx } = this;
const basics = await ctx.prometheusModel.CreditCallriskReport.one({ where: { report_id } });
const basics = await ctx.prometheusModel.CreditCallriskReport.one({ where: { id: report_id } });
if (!basics || !basics.id) {
ctx.failed('error report_id');
}
......
......@@ -64,6 +64,197 @@ class CommonService extends Service {
return sign;
}
async unifiedOrder(type, order_id) {
const { ctx } = this;
const order = await ctx.service.credit.order.getOrderInfo(order_id);
const price = order.price * 100 - order.preferential_price * 100;
console.info(price);
if (price <= 0) {
ctx.failed('error price');
}
if (type === 'wexin') {
let body = '黑名单报告检测支付';
if (order.type === 2) {
body = '个人通话风险检测支付';
}
const data = {
body,
order_id,
trade_no: moment().valueOf() + ctx.helper.PrefixInteger(order_id, 11),
total_fee: price,
notify_url: '',
product_id: order.order_no,
scene_info: { h5_info: { type: 'Wap', wap_url: this.config.PHP_URL, wap_name: '我的信用' }, },
};
const ret = await this.WexinUnifiedOrder(data);
const pay_data = {
order_id,
trade_no: data.trade_no,
description: data.body,
prepay_id: ret.prepay_id || 0,
amount: data.total_fee,
unifiedorder_result: JSON.stringify(ret),
client_ip: ctx.helper.getClientIP(),
};
await this.addCreditPay(pay_data);
return { url: ret.mweb_url };
} else if (type === 'alipay') {
//TODO
}
return false;
}
async WexinUnifiedOrder(params) {
const { ctx } = this;
const { body, trade_no, total_fee, notify_url, product_id, scene_info } = params;
const appid = '';
const mch_id = '';
const trade_type = 'MWEB';
const nonce_str = ctx.helper.createNoncestr();
const spbill_create_ip = ctx.helper.getClientIP();
let json_data = {
appid,
mch_id,
body,
trade_no,
total_fee,
spbill_create_ip,
notify_url,
product_id,
scene_info,
trade_type,
nonce_str,
};
const sign = this.getWexinSign(json_data);
json_data.sign = sign;
const xml_data = ctx.helper.jsonToxml(json_data);
const url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
const result_wexin = await ctx.curl(url, { timeout: 3000, method: 'POST', data: xml_data, headers: { 'content-type': 'text/html', }, streaming: false, dataType: 'text', });
console.info(result_wexin);
if (result_wexin.status !== 200) {
ctx.failed('wexin pay failed');
}
const result = await ctx.helper.xmlTojson(result_wexin.data);
ctx.logger.info({ pay_wexin_params: json_data, pay_wexin_result: result });
let ret = {};
for (let key in result) {
ret[key] = result[key][0];
}
let error_msg = 'wexin pay error';
if (!ret.return_code || ret.return_code !== 'SUCCESS') {
error_msg = ret.return_msg || error_msg;
// ctx.failed(error_msg);
}
if (!ret.result_code || ret.result_code !== 'SUCCESS') {
error_msg = ret.err_code_des || error_msg;
// ctx.failed(error_msg);
}
return ret;
}
async addCreditPay(params) {
const { ctx } = this;
const { order_id, trade_no, prepay_id, amount, description, unifiedorder_result, client_ip } = params;
const data = {
order_id,
trade_no,
prepay_id,
amount,
description,
unifiedorder_result,
client_ip,
}
const ret = await ctx.prometheusModel.CreditPay.add(data);
return ret;
}
async alipay() {
}
async WexinCheckPay(params) {
const { ctx } = this;
const { transaction_id, out_trade_no, nonce_str } = params;
}
/**
* @description 支付回调通知
* @param {*} result
*/
async WexinNotify(result) {
const { ctx } = this;
// && result.appid === this.config.MCH_APPID
if (result) {
let sign_data = {};
for (let key in result) {
sign_data[key] = result[key][0];
}
if (sign_data.return_code && sign_data.return_code === 'SUCCESS' && sign_data.result_code && sign_data.result_code === 'SUCCESS') {
const wexin_return_sign = sign_data.sign;
delete sign_data.sign;
const sign = this.getSign(sign_data);
const out_trade_no = sign_data.out_trade_no;
const total_fee = sign_data.total_fee;
const pay_info = await ctx.prometheusModel.CreditPay.findOne({ where: { trade_no: out_trade_no }, order: [['id', 'desc']] });
if (!pay_info || !pay_info.order_id) {
ctx.failed('error pay');
}
const order = await ctx.prometheusModel.CreditOrder.findOne({ where: { id: pay_info.order_id } });
if (!order || !order.id) {
ctx.failed('error order');
}
const price = order.price * 100 - order.preferential_price * 100;
if (total_fee == price && sign == wexin_return_sign) {
const state_time = moment().format('YYYY-MM-DD HH:mm:ss');
await ctx.prometheusModel.CreditPay.edit({ where: { id: pay_info.id }, params: { pay_result: JSON.stringify(result), status: 1 } });
await ctx.prometheusModel.CreditOrder.update({ state: '已支付', state_time: state_time, pay_status: 1 }, { where: { id: order.id } });
console.info('7777777777777777777777');
return true;
}
}
}
return false;
}
/**
* @author lisk
* @description 参数签名
* @param {*} params
*/
getWexinSign(params) {
const { ctx, app } = this;
let ret = [];
for (let val of Object.keys(params).sort()) {
ret[val] = params[val];
}
let str = '';
for (let key in ret) {
str += key + '=' + ret[key] + '&';
}
const mch_key = this.config.MCH_KEY;
const sign = ctx.helper.md5(str + 'key=' + mch_key).toLocaleUpperCase();
return sign;
}
}
module.exports = CommonService;
......@@ -5,6 +5,7 @@ const Service = require('egg').Service;
const moment = require('moment');
const _ = require('lodash');
class OrderService extends Service {
//历史订单
......@@ -119,6 +120,26 @@ class OrderService extends Service {
return valid;
}
async getOrderInfo(order_id) {
const { ctx } = this;
const order = await ctx.prometheusModel.CreditOrder.findOne({ where: { id: order_id } });
if (!order || !order.id) {
ctx.failed('error order');
}
return order;
}
async checkOrderPay(order_id) {
const { ctx } = this;
const order = await ctx.prometheusModel.CreditOrder.findOne({ where: { id: order_id } });
if(order && order.pay_status === 1){
return true;
}
}
}
......
......@@ -25,7 +25,8 @@
"parameter": "^3.3.1",
"ramda": "^0.25.0",
"request": "^2.88.0",
"uuid": "^3.3.2"
"uuid": "^3.3.2",
"xml2js": "^0.4.22"
},
"devDependencies": {
"autod": "^3.0.1",
......
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