Authored by 郝肖肖

merage wechat-pay

... ... @@ -13,7 +13,7 @@ const cleanHtml = require(`${global.utils}/cleanHtml`);
const camelCase = global.yoho.camelCase;
const helpers = global.yoho.helpers;
const _ = require('lodash');
const integerTest = /^[0-9]*$/;
/**
* 支付页
... ... @@ -25,12 +25,22 @@ const online = (req, res, next) => {
let orderCode = req.query.code;
let uid = req.user.uid;
if (!integerTest.test(orderCode)) {
// 不合法订单号
return next();
}
PayData.getPayInfo(uid, orderCode).then(result => {
let order = result.order;
let promotion = order.promotionFormulas;
let coin = 0;
let amount = _.toNumber(order.paymentAmount);
if (!order.orderCode) {
// 不合法订单号
return next();
}
_.forEach(promotion, p => {
if (p.promotion === 'YOHO币') {
coin = parseInt(parseFloat(p.promotionAmount.substring(2, p.promotionAmount.length)) * 100, 10);
... ... @@ -47,6 +57,7 @@ const online = (req, res, next) => {
module: 'shopping',
page: 'pay',
title: '支付页面',
bcNavFocus: 3,
username: req.user.username
}, result));
} else {
... ... @@ -54,6 +65,7 @@ const online = (req, res, next) => {
defaultHeader: false,
module: 'shopping',
page: 'pay-over',
bcNavFocus: 3,
content: {
cost: order.paymentAmount,
orderNum: order.orderCode,
... ... @@ -78,17 +90,18 @@ const online = (req, res, next) => {
*/
const toPay = (req, res, next) => {
let orderCode = req.body.code;
let method = req.body.method;
let payType = req.body.payType;
let uid = req.user.uid;
let user = req.user;
method = parseInt(method, 10);
if (!integerTest.test(orderCode)) {
// 不合法订单号
return next();
}
OrderData.orderDetail(uid, orderCode).then(result => {
if (result && result.data) {
let order = camelCase(result.data);
return PayHelpers.pay(user, order, method);
return PayHelpers.pay(user, result.data, payType, req.protocol);
} else {
return {
code: 400,
... ... @@ -101,25 +114,84 @@ const toPay = (req, res, next) => {
};
/**
* 微信扫码支付页面
* @param req
* @param res
* @param next
*/
const weixinQr = (req, res, next) => {
let code = req.query.code;
let url = req.query.url;
let uid = req.user.uid;
if (!integerTest.test(code)) {
// 不合法订单号
return next();
}
OrderData.orderDetail(uid, code).then(result => {
if (result && result.data) {
return camelCase(result.data);
} else {
return {};
}
}).then(order => {
res.display('weixin-pay', {
defaultHeader: false,
module: 'shopping',
page: 'wxpay',
title: '微信扫码支付页面',
code: code,
url: url,
order: order
});
}).catch(next);
};
const weixinPayState = (req, res) => {
let code = req.query.code;
let uid = req.user.uid;
OrderData.orderDetail(uid, code).then(result => {
if (result && result.data && result.data.payment_status === 'Y') {
res.json({
code: 200,
data: {
href: helpers.urlFormat('/me/order/detail', {orderCode: code})
}
});
} else {
return res.json({});
}
}).catch(() => {
return res.json({});
});
};
/**
* 支付成功回调
* @param req
* @param res
*/
const callback = (req, res) => {
const callback = (req, res, next) => {
let type = req.params.type;
let payId = PayData.payments[type];
let query = req.query;
if (!payId) {
return next();
}
PayHelpers.afterPay(query, payId, req.user).then(result => {
if (result.code === 200 && result.data && result.data.order) {
let order = result.data.order;
let promotion = order.promotionFormulas;
let promotion = order.promotion_formulas;
let coin = 0;
_.forEach(promotion, p => {
if (p.promotion === 'YOHO币') {
coin = parseInt(parseFloat(p.promotionAmount.substring(2, p.promotionAmount.length)) * 100, 10);
coin = parseInt(parseFloat(p.promotion_amount.substring(2, p.promotion_amount.length)) * 100, 10);
}
});
... ... @@ -127,24 +199,44 @@ const callback = (req, res) => {
defaultHeader: false,
module: 'shopping',
page: 'pay-over',
bcNavFocus: 3,
content: {
cost: order.paymentAmount,
orderNum: order.orderCode,
cost: order.payment_amount,
orderNum: order.order_code,
coin: coin,
orderHref: helpers.urlFormat('/me/order/detail', {
orderCode: order.orderCode
orderCode: order.order_code
}),
walkHref: helpers.urlFormat('/')
}
});
} else {
res.display('pay-fail', {
defaultHeader: false,
payName: result.payName,
bcNavFocus: 3
});
}
});
}).catch(next);
};
// 支付确认
const sendPayConfirm = (req, res, next) => {
let code = req.body.code;
let payment = req.body.payment;
let uid = req.user.uid;
PayData.sendPayConfirm(code, payment, uid).then(result => {
res.json(result);
}).catch(next);
};
module.exports = {
online,
callback,
toPay
toPay,
weixinQr,
weixinPayState,
sendPayConfirm
};
... ...
... ... @@ -4,7 +4,7 @@
'use strict';
const Bank = {
getList: () => {
getList() {
return {
BOCB2C: {
name: '中国银行',
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/7/22
*/
'use strict';
const config = global.yoho.config;
const helpers = global.yoho.helpers;
const common = require('./common');
const sign = require('./sign');
const payHelpersBank = require('../bank');
const md5 = require('md5');
const logger = global.yoho.logger;
const ALIPAY_URL = 'https://mapi.alipay.com/gateway.do';
const Alibank = {
pay(user, order, param, protocol) {
let payParams = JSON.parse(param.payParams);
let params = {
service: 'create_direct_pay_by_user',
partner: payParams.merchant_id,
_input_charset: 'utf-8',
notify_url: config.pay.serviceNotify + 'payment/alipay_notify',
return_url: protocol + ':' + helpers.urlFormat('/shopping/pay/callback/alibank'),
subject: 'BLK订单号:' + order.order_code,
out_trade_no: order.order_code,
it_b_pay: common.getPayExpireMin(order.pay_expire) + 'm',
total_fee: order.payment_amount,
payment_type: '1',
defaultbank: param.bankCode,
seller_email: payParams.merchant_other_code,
sign_id_ext: user.uid,
sign_name_ext: user.username
};
// TODO 防钓鱼配置,参考php
let signStr = md5(sign.raw(params) + payParams.merchant_key);
let body = sign.rawUncode(params) + '&sign=' + signStr + '&sign_type=MD5';
return {
code: 200,
data: {
href: ALIPAY_URL + '?' + body
}
};
},
notify(data, param) {
let payParams = JSON.parse(param.payParams);
let outTradeNos = data.out_trade_no && data.out_trade_no.split('_');
let orderCode = parseInt(outTradeNos[0], 10);
let bankName = outTradeNos[1] && payHelpersBank.getList()[outTradeNos[1]].name || '';
logger.info(`Alipay notify, params = ${JSON.stringify(data)}`);
if (!this.checkNotify(data, payParams)) {
return {payResult: -1, bankName: bankName};
} else {
return {
bankName: bankName,
orderCode: orderCode,
payResult: data.trade_status === 'TRADE_SUCCESS' ? 200 : 400,
payTime: data.gmt_payment || '',
totalFee: data.total_fee,
resultMsg: data.notify_type,
payOrderCode: orderCode,
tradeNo: data.trade_no,
bankBillNo: ''
};
}
},
checkNotify(data, payParams) {
let signValue = data.sign;
delete data.sign;
delete data.sign_type;
delete data.code;
let signStr = md5(sign.raw(data) + payParams.merchant_key);
return signValue === signStr;
}
};
module.exports = Alibank;
... ...
... ... @@ -17,7 +17,7 @@ const ALIPAY_URL = 'https://mapi.alipay.com/gateway.do';
const Alipay = {
pay(user, order, param) {
pay(user, order, param, protocol) {
let payParams = JSON.parse(param.payParams);
let params = {
... ... @@ -25,11 +25,11 @@ const Alipay = {
partner: payParams.merchant_id,
_input_charset: 'utf-8',
notify_url: config.pay.serviceNotify + 'payment/alipay_notify',
return_url: 'http:' + helpers.urlFormat('/shopping/pay/callback/alipay'),
subject: 'BLK订单号:' + order.orderCode,
out_trade_no: order.orderCode,
it_b_pay: common.getPayExpireMin(order.payExpire) + 'm',
total_fee: order.paymentAmount,
return_url: protocol + ':' + helpers.urlFormat('/shopping/pay/callback/alipay'),
subject: 'BLK订单号:' + order.order_code,
out_trade_no: order.order_code,
it_b_pay: common.getPayExpireMin(order.pay_expire) + 'm',
total_fee: order.payment_amount,
payment_type: '1',
seller_email: payParams.merchant_other_code,
sign_id_ext: user.uid,
... ... @@ -56,14 +56,16 @@ const Alipay = {
if (!this.checkNotify(data, payParams)) {
return {payResult: -1};
} else {
let orderCode = parseInt(data.out_trade_no, 10);
return {
bankName: '',
orderCode: data.out_trade_no,
orderCode: orderCode,
payResult: data.trade_status === 'TRADE_SUCCESS' ? 200 : 400,
payTime: data.gmt_payment || '',
totalFee: data.total_fee,
resultMsg: data.notify_type,
payOrderCode: data.out_trade_no,
payOrderCode: orderCode,
tradeNo: data.trade_no,
bankBillNo: ''
};
... ...
... ... @@ -5,6 +5,7 @@
*/
'use strict';
const moment = require('moment');
const xml2js = require('xml2js');
const common = {
getPayExpireMin(expire) {
... ... @@ -17,12 +18,55 @@ const common = {
if (diff > 0) {
return Math.floor(diff / 1000 / 60);
} else {
return defaultValue;
return 0;
}
} else {
return defaultValue;
}
},
getPayExpireCouple(expire) {
let start = expire ? moment(expire, 'YYYY-MM-DD HH:mm:ss') : moment();
let end = expire ? moment(expire, 'YYYY-MM-DD HH:mm:ss') : moment();
return {
start: start.subtract(2, 'hours'),
end: end.add(5, 'minutes')
};
},
nonceStr(length) {
length = length || 32;
let chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
let str = '';
for (let i = 0; i < length; i++) {
str += chars[parseInt(Math.random() * length, 10)];
}
return str;
},
toXml(obj, root) {
let xmlBuilder = new xml2js.Builder({
rootName: root || 'xml'
});
return xmlBuilder.buildObject(obj);
},
xml2Obj(xml) {
let xmlParser = new xml2js.Parser({
trim: true,
explicitArray: false
});
return new Promise((resolve, reject) => {
xmlParser.parseString(xml, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
};
... ...
... ... @@ -9,17 +9,21 @@ const Sign = {
raw(args) {
let keys = Object.keys(args);
keys = keys.sort();
keys = keys.filter(k => {
return args[k] === '' || args[k] === 'undefined' ? false : true;
}).sort();
return keys.map(k => {
return k.toLowerCase() + '=' + args[k];
return k + '=' + args[k];
}).join('&');
},
rawUncode(args) {
let keys = Object.keys(args);
keys = keys.sort();
keys = keys.filter(k => {
return args[k] === '' || args[k] === 'undefined' ? false : true;
}).sort();
return keys.map(k => {
return k.toLowerCase() + '=' + encodeURIComponent(args[k]);
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/7/22
* @date: 16/9/8
*/
'use strict';
const ServiceAPI = global.yoho.ServiceAPI;
const helpers = global.yoho.helpers;
const Wechat = {
pay(user, order, info) {
return ServiceAPI.get('payment/weixin_data', {
order_code: order.order_code,
payment_code: info.id,
app_key: 'blkpc'
}).then(result => {
if (result && result.code === 200 && result.data) {
let url = `/shopping/pay/online/weixin?url=${result.data.codeUrl}&code=${order.order_code}`;
return {
code: 200,
data: {
href: helpers.urlFormat(url)
}
};
} else {
return {
code: 400,
message: result.message
};
}
});
}
};
module.exports = Wechat;
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/7/22
*/
'use strict';
const moment = require('moment');
const rq = require('request-promise');
const common = require('./common');
const sign = require('./sign');
const md5 = require('md5');
const _ = require('lodash');
const logger = global.yoho.logger;
const config = global.yoho.config;
const wechatUnifiedOrderUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
const Wechat = {
pay(user, order, info) {
let expire = common.getPayExpireCouple(order.payExpire);
info = JSON.parse(info.payParams || '{}');
let unifiedOrder = {
body: 'BLK订单号:' + order.orderCode,
out_trade_no: order.orderCode,
total_fee: parseInt(order.paymentAmount * 100, 10),
time_start: moment(expire.start).format('YYYYMMDDHHmmss'),
time_expire: moment(expire.end).format('YYYYMMDDHHmmss'),
trade_type: 'NATIVE',
product_id: order.orderCode,
notify_url: config.pay.serviceNotify + 'payment/weixin_notify',
appid: info.app_id,
mch_id: info.partner_id,
nonce_str: common.nonceStr()
};
let signStr = md5(sign.raw(unifiedOrder) + '&key=' + info.partner_key);
unifiedOrder.sign = _.toUpper(signStr);
return this.unifiedOrder(unifiedOrder).then(result => {
console.log(result);
if (result && result.xml) {
let data = result.xml || {};
if (data.return_code === 'SUCCESS' && Wechat.checkSign(data, info)) {
return {
code: 200,
data: {
href: data.code_url
}
};
}
}
return {
code: 400,
message: '请稍后重试'
};
});
},
unifiedOrder(unifiedOrder) {
let xml = common.toXml(unifiedOrder);
return rq({
method: 'POST',
uri: wechatUnifiedOrderUrl,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(xml)
},
body: xml,
timeout: 1000
}).then(result => {
console.log(result);
if (result) {
return common.xml2Obj(result);
} else {
return {};
}
}).catch(err => {
logger.error(`call wechat pay unifiedOrder fail. order=${JSON.stringify(unifiedOrder)}`, err);
return {};
});
},
notify(data, info) { // eslint-disable-line
},
checkSign(data, info) {
if (data && data.sign) {
let signStr = data.sign || '';
delete data.sign;
return signStr === md5(sign.raw(data) + info.partner_key);
}
return false;
}
};
module.exports = Wechat;
... ...
... ... @@ -10,27 +10,71 @@
const PayData = require('../models/pay');
const OrderData = require('../models/order');
const Alipay = require('./pay/alipay');
const Alibank = require('./pay/alibank');
const Wechat = require('./pay/wechat');
const Promise = require('bluebird');
const common = require('./pay/common');
const co = Promise.coroutine;
const logger = global.yoho.logger;
const Payment = {
pay(user, order, method) {
pay(user, order, payType, protocol) {
return co(function*() {
let payInfo = yield PayData.getPaymentInfo(method);
let result = {
code: 400,
message: '获取支付方式信息失败'
};
let paymentPars = payType.split('_');
let payInfo;
let bankCode = '';
if (payInfo && payInfo.id === method) {
if (method === PayData.payments.alipay) {
result = Alipay.pay(user, order, payInfo);
if (paymentPars.length !== 2) {
return result;
}
if (!order.order_code) {
result.message = '没有找到该订单';
return result;
}
if (order.is_cancel && order.is_cancel === 'Y') {
result.message = '该订单已经取消';
return result;
}
if (order.pay_expire && common.getPayExpireMin(order.pay_expire) <= 0) {
result.message = '当前订单不可支付';// 该订单已超过2个小时
return result;
}
let method = paymentPars[0] * 1;
if (method === PayData.payments.wechat) {
// 如果是微信支付,不需要调用获取支付方式详情接口
result = yield Wechat.pay(user, order, {id: PayData.payments.wechat});
} else {
payInfo = yield PayData.getPaymentInfo(method);
if (!payInfo.payParams) {
return result;
}
switch (payInfo.id) {
case PayData.payments.alipay:
result = Alipay.pay(user, order, payInfo, protocol);
break;
case PayData.payments.alibank:
bankCode = paymentPars[1];
payInfo.bankCode = bankCode;// 设置默认银行
result = Alibank.pay(user, order, payInfo, protocol);
break;
default:
break;
}
}
if (result.code === 200) {
let updateInfo = yield Payment.beforePay(user, order, method);
let updateInfo = yield Payment.beforePay(user, order, method, bankCode);
if (updateInfo && updateInfo.code !== 200) {
return updateInfo;
... ... @@ -41,17 +85,15 @@ const Payment = {
})();
},
beforePay(user, order, method) {
beforePay(user, order, method, bankCode) {
return Promise.all([
OrderData.updateOrderPayment(order.orderCode, method, user.uid),
PayData.savePrePayInfo(order.orderCode, method, user.uid),
PayData.getBankByOrder(order.orderCode)
OrderData.updateOrderPayment(order.order_code, method, user.uid),
PayData.savePrePayInfo(order.order_code, method, user.uid),
PayData.getBankByOrder(order.order_code)
]).then(result => {
let paymentRecord = result[0];
let prePayResult = result[1];
let bankRecord = result[2];
let bankCode = ''; // 暂时写成'', 参考php代码 Payment.php:564
if (!paymentRecord || paymentRecord.code !== 200 || !prePayResult || prePayResult.code !== 200) {
let message = paymentRecord && paymentRecord.message ? paymentRecord.message : '系统繁忙,请稍后再试';
... ... @@ -59,11 +101,10 @@ const Payment = {
return {code: 400, message: message};
}
if (bankRecord && bankRecord.bankCode) {
return PayData.updateOrderPayBank(order.orderCode, method, bankCode);
return PayData.updateOrderPayBank(order.order_code, method, bankCode);
} else {
return PayData.setOrderPayBank(order.orderCode, method, bankCode);
return PayData.setOrderPayBank(order.order_code, method, bankCode);
}
}).catch(e => {
... ... @@ -80,27 +121,35 @@ const Payment = {
return co(function*() {
let payInfo = yield PayData.getPaymentInfo(payId);
let payResult = {};
let payData = {};
let payName = '';
if (payId === PayData.payments.alipay) {
payResult = Alipay.notify(query, payInfo);
payResult.bankName = payResult.bankName || payInfo.payName || '';
payResult.bankCode = payResult.bankCode || payInfo.pay_code || '';
} else if (payId === PayData.payments.alibank) {
payResult = Alibank.notify(query, payInfo);
}
payResult.bankName = payName = payResult.bankName || payInfo.payName || '';
payResult.bankCode = payResult.bankCode || payInfo.pay_code || '';
if (payResult && payResult.payResult === 200) {
if (payResult.orderCode) {
logger.info('pay back confirm');
yield PayData.sendPayConfirm(payResult.orderCode, payId, user.uid);
}
return yield PayData.procOrderData(payResult, user.uid);
payData = yield PayData.procOrderData(payResult, user.uid);
} else {
return {
payData = {
code: 500,
message: '支付失败'
};
}
payData.payName = payName;
return payData;
})();
}
};
... ...
... ... @@ -8,19 +8,22 @@
const api = global.yoho.API;
// 获取支付宝等平台支付方式列表
const getPayProvider = () => {
return api.get('', {
method: 'web.SpaceOrders.getPaymentList'
});
}, {cache: true});
};
// 获取单个支付方式相关详细信息
const getPaymentInfo = (id) => {
return api.get('', {
method: 'web.SpaceOrders.getPaymentById',
id: id
});
}, {cache: true});
};
/* 获取上次使用的支付方式*/
const getBankByOrder = (code) => {
return api.get('', {
method: 'web.SpaceOrders.getOrderPayBank',
... ... @@ -28,6 +31,7 @@ const getBankByOrder = (code) => {
});
};
/* 记录支付方式*/
const setOrderPayBank = (code, payment, bankCode) => {
return api.get('', {
method: 'web.SpaceOrders.addOrderPayBank',
... ... @@ -37,6 +41,7 @@ const setOrderPayBank = (code, payment, bankCode) => {
});
};
/* 更改支付方式*/
const updateOrderPayBank = (code, payment, bankCode) => {
return api.get('', {
method: 'web.SpaceOrders.modifyOrderPayBank',
... ... @@ -46,6 +51,7 @@ const updateOrderPayBank = (code, payment, bankCode) => {
});
};
/* 发送支付确认*/
const sendPayConfirm = (code, payment, uid) => {
return api.get('', {
method: 'app.SpaceOrders.payConfirm',
... ... @@ -55,6 +61,7 @@ const sendPayConfirm = (code, payment, uid) => {
});
};
/* 选择支付,校验时间间隔,插入数据,为用户取消订单做准备<用户支付取消订单,10分钟间隔>。*/
const savePrePayInfo = (code, payment, uid) => {
return api.get('', {
method: 'app.order.savePrePayInfo',
... ...
... ... @@ -12,13 +12,16 @@ const Promise = require('bluebird');
const co = Promise.coroutine;
const camelCase = global.yoho.camelCase;
const OrderData = require('./order');
const payHelpersBank = require('./../helpers/bank');
const logger = global.yoho.logger;
const _ = require('lodash');
const payments = {
alipay: 33
alipay: 33,
wechat: 36,
alibank: 12
};
/**
... ... @@ -27,34 +30,47 @@ const payments = {
*/
const getOnlinePayProvider = () => {
return api.getPayProvider().then(result => {
let payPattern = [], nav = [];
if (!(result.code && result.code === 200)) {
return [];
return payPattern;
}
let online = _(result.data)
.filter(i => _.includes([payments.alipay], i.id))
.map(i => {
return {
id: i.id,
name: i.payName,
img: i.payIcon,
selected: i.id === payments.alipay
};
})
.value();
let nav = [
{
id: 'onlinePay',
name: '支付宝等平台',
selected: true
// 处理支付宝等平台数据
_.each(result.data, val => {
// 去除icon为空的
if (_.isEmpty(val.payIcon)) {
return true;
}
];
return {
type: nav,
onlinePay: online
};
nav.push({
id: val.id,
name: val.payName,
value: val.id + '_platform',
img: val.payIcon
});
});
payPattern.push({
name: '支付宝等平台',
children: nav
});
// 处理使用银行卡数据
nav = [];
_.each(payHelpersBank.getList(), (val, key) => {
nav.push({
id: payments.alibank,
name: val.name,
value: payments.alibank + '_' + key,
img: val.ico
});
});
payPattern.push({
name: '使用银行卡',
children: nav
});
return payPattern;
});
};
... ... @@ -64,14 +80,27 @@ const getOnlinePayProvider = () => {
* @param code
*/
const getOrderInfo = (uid, code) => {
return co(function *() {
let orderData = yield OrderData.orderDetail(uid, code);
return OrderData.orderDetail(uid, code).then(orderData => {
if (orderData && orderData.data) {
return camelCase(orderData.data);
} else {
return {};
}
});
};
/**
* 获取订单支付银行信息
* @param id
*/
const getBankByOrder = (id) => {
return co(function *() {
let data = yield api.getBankByOrder(id);
if (data && data.code === 200 && data.data) {
return camelCase(data.data);
}
return {};
})();
};
... ... @@ -81,14 +110,29 @@ const getOrderInfo = (uid, code) => {
* @param code
*/
const getPayInfo = (uid, code) => {
return co(function *() {
let payment = yield Promise.all([getOnlinePayProvider(), getOrderInfo(uid, code)]);
return Promise.all([getOnlinePayProvider(), getOrderInfo(uid, code), getBankByOrder(code)])
.then(payment => {
let paymentId = parseInt(payment[2].payment || 0, 10);
let findIndex;
// 渲染选择的默认支付方式
if (paymentId === payments.alibank && payment[0][1] && payment[2]) { // 银行支付
findIndex = _.findIndex(payment[0][1].children, {value: payments.alibank + '_' + payment[2].bankCode});
findIndex = findIndex > 0 ? findIndex : 0;
payment[0][1].selected = true;
payment[0][1].children[findIndex].selected = true;
} else if (payment[0] && payment[0][0]) { // 支付宝等平台
findIndex = _.findIndex(payment[0][0].children, {id: paymentId});
findIndex = findIndex > 0 ? findIndex : 0;
payment[0][0].selected = true;
payment[0][0].children[findIndex].selected = true;
}
return {
pay: payment[0],
order: payment[1]
};
})();
return {
pay: payment[0],
order: payment[1]
};
});
};
/**
... ... @@ -107,21 +151,6 @@ const getPaymentInfo = (id) => {
};
/**
* 获取订单支付银行信息
* @param id
*/
const getBankByOrder = (id) => {
return co(function *() {
let data = yield api.getBankByOrder(id);
if (data && data.code === 200 && data.data) {
return camelCase(data.data);
}
return {};
})();
};
/**
* 设置订单支付银行
* @param code
* @param payment
... ... @@ -195,8 +224,8 @@ const procOrderData = (payResult, uid) => {
let orderInfo = yield OrderData.orderDetail(uid, orderCode);
if (orderInfo && orderInfo.data) {
let order = camelCase(orderInfo.data);
let amount = order.paymentAmount;
let order = orderInfo.data;
let amount = order.payment_amount;
if (order.is_cancel === 'Y') {
logger.warn('front pay success but order is cancel.', {payResult: payResult, order: order});
... ...
... ... @@ -32,8 +32,10 @@ router.post('/order/submit', auth, order.orderSub);
// 支付
router.get('/pay/online', auth, pay.online);
router.get('/pay/online/weixin', auth, pay.weixinQr);
router.get('/pay/online/weixin/state', auth, pay.weixinPayState);
router.post('/pay/online/go', auth, pay.toPay);
router.post('/pay/online/sendPayConfirm', auth, pay.sendPayConfirm);
// 支付回调
router.get('/pay/callback/:type', auth, pay.callback);
... ...
{{> settle-header}}
<div class="pay-page pay-fail-page blk-page">
<div class="center-content">
{{> bc-nav}}
<div class="pay-fail-group">
<div class="pay-fail-bg"></div>
<p class="pay-fail-info big">抱歉!订单支付失败,请立即联系客服</p>
<p class="pay-fail-info small">I'm sorry! Order failed to pay, please contact customer service</p>
<p class="pay-fail-info small">感谢您选择{{payName}}支付方式,我们的客服随后会和您确认订单</p>
<p class="pay-fail-info medium">客服电话:<span class='blue'>400-889-9646</span></p>
<a href="/" class="go-to-home">
<span class="btn">返回BLK首页</span>
</a>
</div>
</div>
</div>
... ...
{{> settle-header}}
<div class="pay-success-page blk-page">
<div class="pay-page pay-success-page blk-page">
<div class="center-content">
{{> bc-nav}}
{{# content}}
<div class="order-info clearfix">
<div class="left">
... ...
{{> settle-header}}
<div class="center-content pay-online-wrapper">
{{> bc-nav}}
{{# order}}
<div class="title">
<div class="content">
... ... @@ -8,10 +10,8 @@
</div>
<div class="footer">
<span class="desc pay-notice left">{{../username}}如果<span class="blue">2小时</span>内您无法完成付款,系统会将您的订单取消</span>
<span id="order-detail-ctrl" class="right order-detail-ctrl">
<em>收起详情</em>
<i class="iconfont down">&#xe616;</i>
<i class="iconfont up">&#xe617;</i>
<span id="order-detail-ctrl" class="blue right order-detail-ctrl">
订单详情<i class="iconfont down">&#xe616;</i>
</span>
</div>
</div>
... ... @@ -46,28 +46,31 @@
</div>
{{/order}}
{{# pay}}
{{#if pay}}
<div class="pay-type">
<div class="pay-nav">
<ul class="tabs clearfix">
{{#each type}}
<li{{#if selected}} class="active"{{/if}} data-type="{{id}}">{{name}}</li>
{{/each}}
{{# pay}}
<li{{#if selected}} class="active"{{/if}}>{{name}}</li>
{{/pay}}
</ul>
</div>
<div id="online">
{{# onlinePay}}
<div class="pay-type-icon online-pay{{#if selected}} active{{/if}}" data-name="{{name}}" data-id="{{id}}">
<img src="{{img}}">
<span class="choose-tag iconfont">&#xe63b;</span>
<div class="pay-icon-content">
{{# pay}}
<div class="pay-icon {{#unless selected}} hide{{/unless}}">
{{# children}}
<div class="pay-type-icon online-pay{{#if selected}} active{{/if}}" data-name="{{name}}" data-id="{{id}}" data-value="{{value}}">
<img src="{{img}}">
<span class="choose-tag iconfont">&#xe63b;</span>
</div>
{{/children}}
</div>
{{/ onlinePay}}
{{/pay}}
</div>
</div>
{{/ pay}}
{{/if}}
<div class="pay-ctrl">
<span id="go-pay-btn" class="btn btn-shape right" data-order="{{order.orderCode}}"><em id="pay-type-name">支付宝</em>支付</span>
<span id="go-pay-btn" class="btn btn-shape right" data-order="{{order.orderCode}}"><em id="pay-type-name">前往支付宝</em></span>
</div>
</div>
... ...
{{> settle-header}}
<div class="center-content pay-online-wrapper">
{{> bc-nav}}
{{# order}}
<div class="title">
<div class="content">
<span class="desc left">订单已提交成功,请您尽快付款!订单编号:{{orderCode}}</span>
<span class="cash right">应付金额:{{paymentAmount}}</span>
</div>
<div class="footer">
<span class="desc pay-notice left">{{../username}}如果<span class="blue">2小时</span>内您无法完成付款,系统会将您的订单取消</span>
<span id="order-detail-ctrl" class="blue right order-detail-ctrl">
订单详情<i class="iconfont down">&#xe616;</i>
</span>
</div>
</div>
<div class="order-detail">
<div class="row">
<div class="item receiver-address">
<span class="label"> 收货地址:</span>
<span>{{area}} {{address}}</span>
</div>
</div>
<div class="row">
<div class="item">
<span class="label">&nbsp;&nbsp;&nbsp;&nbsp;人:</span>
<span>{{userName}}</span>
</div>
<div class="item">
<span class="label">联系方式:</span>
<span>{{mobile}}</span>
</div>
</div>
<div class="row">
<div class="item">
<span class="label">支付方式:</span>
<span>在线支付</span>
</div>
<div class="item">
<span class="label">送货时间:</span>
<span>{{deliveryTime}}</span>
</div>
</div>
</div>
{{/order}}
<div class="qr-content">
<h1>微信支付</h1>
<div class="qr-content-gruop">
<div class='qr-content-left'>
<div class="qr-code" data-qr="{{url}}" data-code="{{order.orderCode}}"></div>
<div class="qr-content-footer">
<p>请使用微信扫一扫</p>
<p>扫描二维码支付</p>
</div>
</div>
</div>
<h3>
<a href="/shopping/pay/online?code={{order.orderCode}}" class="blue w-pay-change">
<i>&lt;</i>
选择其他支付方式
</a>
</h3>
</div><!--/qr-content-->
</div>
... ...
<div class="cart-bc">
<ul class="breadcrumb">
<li class="level-1{{#isEqual bcNavFocus 1}} current{{/isEqual}}"><a href="javascript:void(0)" >我的购物袋<em></em><i></i></a></li>
<li class="level-2{{#isEqual bcNavFocus 2}} current{{/isEqual}}"><a href="javascript:void(0)">填写核对订单信息<em></em><i></i></a></li>
<li class="level-3{{#isEqual bcNavFocus 3}} current{{/isEqual}}"><a href="javascript:void(0)">成功提交订单<em></em><i></i></a></li>
<li class="level-1{{#isEqual bcNavFocus 1}} current{{/isEqual}}">
<a href="javascript:void(0)" >
我的购物袋
</a>
</li>
<li class="level-2{{#isEqual bcNavFocus 2}} current{{/isEqual}}">
<a href="javascript:void(0)" >
填写核对订单信息
</a>
</li>
<li class="level-3{{#isEqual bcNavFocus 3}} current{{/isEqual}} ie8-last-bug">
<a href="javascript:void(0)" >
成功提交订单
</a>
</li>
</ul>
</div>
\ No newline at end of file
... ...
... ... @@ -32,6 +32,11 @@ module.exports = {
singleApi: 'http://api-test3.yohops.com:9999/',
liveApi: 'http://testapi.live.yohops.com:9999/',
search: 'http://search.yohoops.org/yohosearch/'
// singleApi: 'http://single.yoho.cn/',
// api: 'http://api-test3.yohops.com:9999/',
// service: 'http://service-test3.yohops.com:9999/',
// search: 'http://search.yohoops.org/yohosearch/'
},
useOneapm: false,
useCache: false,
... ...
... ... @@ -61,6 +61,7 @@
"uuid": "^2.0.2",
"winston": "^2.2.0",
"winston-daily-rotate-file": "^1.1.4",
"xml2js": "^0.4.17",
"xss": "^0.2.13",
"yoho-node-lib": "0.2.0"
},
... ...
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 204 204" style="enable-background:new 0 0 204 204;" xml:space="preserve">
<style type="text/css">
.st0{fill:#b0b0b0;}
</style>
<path id="XMLID_8_" class="st0" d="M102,204C45.8,204,0,158.2,0,102C0,45.8,45.8,0,102,0s102,45.8,102,102
C204,158.2,158.2,204,102,204z M102,4C48,4,4,48,4,102c0,54,44,98,98,98c54,0,98-44,98-98C200,48,156,4,102,4z"/>
<g id="XMLID_67_">
<rect id="XMLID_7_" x="100" y="50.2" class="st0" width="4" height="84.2"/>
<rect id="XMLID_6_" x="100" y="143.8" class="st0" width="4" height="10"/>
</g>
</svg>
... ...

3.5 KB | W: | H:

1.66 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
... ... @@ -4,20 +4,27 @@
var $ = require('yoho-jquery');
var $orderDetail = $('.order-detail');
var $orderDetail = $('.order-detail'),
$orderDetailCtrl = $('#order-detail-ctrl');
var Dialog = require('../plugins/dialog').Dialog;
var Dialog = require('../plugins/dialog');
var $payTypeIcon = $('.pay-type-icon'),
$payIconContent = $('.pay-icon-content'),
$goPayBtn = $('#go-pay-btn'),
$payTypeName = $goPayBtn.find('#pay-type-name'),
$payIcon = $payIconContent.find('.pay-icon');
var tpl = '<div class="pay-page-tips">' +
'<h3>请您在新打开的页面完成付款</h3>' +
'<p>付款完成前请不要关闭此窗口</p>' +
'<p>完成付款后请根据您的情况点击下面的按钮</p>' +
'<div><a href="/me/order"><span class="btn">已完成付款</span></a>' +
'<div><a href="javascript:void(0);" class="pay-over"><span class="btn">已完成付款</span></a>' +
'<span class="btn white close-btn">更换支付方式</span>' +
'</div>' +
'</div>';
var infoDaialog = new Dialog({
var infoDaialog = new Dialog.Dialog({
className: 'pay-info-dialog',
content: tpl,
keep: true
... ... @@ -25,26 +32,21 @@ var infoDaialog = new Dialog({
// 展开详情/收起详情
$('#order-detail-ctrl').click(function() {
$orderDetailCtrl.click(function() {
var $this = $(this);
$this.toggleClass('shrink');
if ($this.hasClass('shrink')) {
// 收起状态
$this.find('em').text('展开详情');
} else {
if ($orderDetail.is(':hidden')) {
// 展开状态
$this.find('em').text('收起详情');
$this.html('收起详情<i class="iconfont up">&#xe617;</i>');
} else {
// 收起状态
$this.html('订单详情<i class="iconfont down">&#xe616;</i>');
}
$orderDetail.toggleClass('hide');
$orderDetail.slideToggle('slow');
});
$('.pay-type-icon').click(function() {
$payTypeIcon.click(function() {
var $this = $(this);
if ($this.hasClass('active')) {
... ... @@ -52,12 +54,28 @@ $('.pay-type-icon').click(function() {
}
// 切换选中状态
$this.siblings('.active').removeClass('active');
$payTypeIcon.removeClass('active');
$this.addClass('active');
$('#pay-type-name').text($this.data('name'));
$payTypeName.text('去' + $this.data('name').replace(/支付$/g, '') + '支付');
});
// 支付按钮 设置默认文字
if ($payIconContent.find('.pay-type-icon.active').length) {
$payTypeName.text('去' + $payIconContent.find('.pay-type-icon.active').data('name').replace(/支付$/g, '') + '支付');
}
// 切换支付方式tabs 委托事件
$('.pay-nav .tabs').click(function(event) {
var $li = $(event.target).closest('li');
if ($li.length <= 0) {
return true;
}
$li.addClass('active').siblings('li').removeClass('active');
$payIcon.eq($li.index()).removeClass('hide').siblings('.pay-icon').addClass('hide');
});
function showDialog() {
infoDaialog.show();
... ... @@ -67,8 +85,8 @@ function showDialog() {
}
// 去支付
$('#go-pay-btn').click(function() {
var payType = $('.pay-type-icon.active').data('id');
$goPayBtn.click(function() {
var payType = $('.pay-type-icon.active').data('value');
var order = $(this).data('order');
$.ajax({
... ... @@ -77,13 +95,31 @@ $('#go-pay-btn').click(function() {
async: false,
data: {
code: order,
method: payType
payType: payType
}
}).then(function(data) {
if (data.code === 200) {
window.open(data.data.href);
showDialog();
} else {
new Dialog.Alert(data.message).show();
}
});
});
// 发送支付确认
$('.pay-info-dialog').on('click', '.pay-over', function() {
var payment = $('.pay-type-icon.active').data('id');
var order = $goPayBtn.data('order');
$.ajax({
type: 'POST',
url: '/shopping/pay/online/sendPayConfirm',
data: {
code: order,
payment: payment
}
}).then(function() {
location.href = '/me/order';
});
});
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/9/8
*/
var $orderDetail = $('.order-detail'),
$qrCode = $('.qr-code'),
$orderDetailCtrl = $('#order-detail-ctrl');
require('yoho-jquery-qrcode');
$(function() {
// 展开详情/收起详情
$orderDetailCtrl.click(function() {
var $this = $(this);
if ($orderDetail.is(':hidden')) {
// 展开状态
$this.html('收起详情<i class="iconfont up">&#xe617;</i>');
} else {
// 收起状态
$this.html('订单详情<i class="iconfont down">&#xe616;</i>');
}
$orderDetail.slideToggle('slow');
});
$qrCode.qrcode({
render: 'div',
size: 270,
text: $qrCode.data('qr')
});
function queryOrderState() {
var code = $('.qr-code').data('code');
$.get('/shopping/pay/online/weixin/state', {code: code}, function(data) {
if (data && data.code === 200) {
location.href = data.data.href;
}
});
}
setInterval(queryOrderState, 2500);
});
... ...
.blk-cart-page,
.shopping-order-page {
.cart-bc {
width: 100%;
position: relative;
background: #999;
top: -2px;
}
.cart-bc {
width: 100%;
position: relative;
background: #999;
top: -2px;
.breadcrumb {
list-style: none;
... ... @@ -14,13 +11,30 @@
padding: 0;
height: 32px;
.current {
li.current {
background: #1b1b1b;
color: #fff;
z-index: 1;
i {
border-color: #fff transparent #fff #1b1b1b;
&:after,
&:before {
content: "";
position: absolute;
width: 0;
height: 0;
top: 0;
}
&:after {
right: 0;
border: 16px solid #fff;
border-left-color: #1b1b1b;
border-right: 0;
}
&:before {
left: 0;
border: 16px solid #1b1b1b;
border-left-color: #fff;
}
}
... ... @@ -32,47 +46,43 @@
z-index: 2;
font-weight: bold;
font-size: 14px;
line-height: 32px;
&:last-child {
i {
right: -17px;
}
}
line-height: 35px;
a {
color: #fff;
width: 100%;
display: inline-block;
position: relative;
z-index: 0;
}
em {
&:after,
&:before {
content: "";
position: absolute;
right: -24px;
top: -8px;
width: 0;
height: 0;
line-height: 0;
border-width: 24px 0 24px 24px;
border-color: transparent transparent transparent #fff;
border-style: dashed dashed dashed solid;
top: 0;
}
i {
position: absolute;
right: -6px;
right: -16px\9; /* stylelint-disable-line */
top: 0;
top: -2px\9; /* stylelint-disable-line */
width: 0;
height: 0;
line-height: 0;
border-width: 17px 0 17px 17px;
border-color: #fff transparent #fff #999;
border-style: dashed dashed dashed solid;
z-index: 1000;
&:after {
right: 0;
border: 16px solid #fff;
border-left-color: #999;
border-right: 0;
}
&:before {
left: 0;
border: 16px solid #999;
border-left-color: #fff;
}
&.ie8-last-bug:after {
display: none;
}
&:first-child:before {
display: none;
}
}
... ...
... ... @@ -15,3 +15,4 @@
/* 支付流程-在线支付 */
@import "pay";
@import "pay-success";
@import "pay-fail";
... ...
.pay-fail-page {
.pay-fail-group {
margin: 145px auto;
text-align: center;
.pay-fail-bg {
background: resolve("layout/pay-fail.svg") no-repeat center center;
width: 115px;
height: 130px;
margin: 0 auto 30px;
}
.go-to-home .btn {
margin-top: 40px;
display: inline-block;
width: 130px;
height: 40px;
line-height: 40px;
}
.big {
font-size: 20px;
line-height: 30px;
color: #212121;
}
.medium {
font-size: 14px;
line-height: 30px;
color: #1b1b1b;
}
.small {
font-size: 14px;
line-height: 40px;
color: #212121;
}
}
}
... ...
... ... @@ -5,6 +5,8 @@ $liBorderColor: #e6e6e6;
$fontColor: #616161;
.pay-online-wrapper {
overflow: hidden;
.title {
margin-top: 35px;
margin-bottom: 20px;
... ... @@ -29,25 +31,6 @@ $fontColor: #616161;
font-size: 14px;
cursor: pointer;
.up {
display: none;
}
.down {
display: inline-block;
}
/* 默认情况为展开状态 箭头方向为下 */
&.shrink {
.up {
display: inline-block;
}
.down {
display: none;
}
}
.iconfont {
font-size: 12px;
margin-left: 10px;
... ... @@ -55,11 +38,10 @@ $fontColor: #616161;
}
.order-detail {
border: 1px solid $liBorderColor;
margin-bottom: 30px;
height: calc(135px + 2px);
border-top: 1px solid $liBorderColor;
padding: 20px 30px;
font-size: 14px;
display: none;
.row {
height: 30px;
... ... @@ -114,12 +96,19 @@ $fontColor: #616161;
}
}
.pay-icon-content {
width: 1200px;
.pay-icon {
min-height: 240px;
}
}
.pay-type-icon {
position: relative;
float: left;
width: 250px;
height: 80px;
margin-bottom: 30px;
height: 60px;
margin-right: 50px;
margin-top: 30px;
cursor: pointer;
... ... @@ -128,7 +117,7 @@ $fontColor: #616161;
img {
display: inline-block;
margin-top: 19px;
margin-top: 9px;
}
.choose-tag {
... ... @@ -161,6 +150,44 @@ $fontColor: #616161;
line-height: @height;
}
}
.qr-content {
border: 1px solid $liBorderColor;
background-color: #fff;
padding: 26px 30px 30px;
margin-top: 6px;
margin-bottom: 85px;
.qr-content-gruop {
width: 300px;
padding: 30px 670px 30px 0;
margin: 0 auto;
overflow: hidden;
background: resolve("shopping/w-p-phone.png") no-repeat top right;
}
.qr-content-left {
width: 300px;
.qr-code {
width: 100%;
height: 300px;
padding: 14px;
border: 1px solid $liBorderColor;
margin-bottom: 20px;
}
.qr-content-footer {
width: 100%;
height: 60px;
color: #fff;
background: resolve("shopping/w-p-word.png") no-repeat 50px 6px #090909;
padding-left: 123px;
line-height: 22px;
padding-top: 10px;
}
}
}
}
.pay-info-dialog {
... ...
... ... @@ -109,6 +109,7 @@
margin-top: 40px;
float: right;
margin-right: 192px;
clear: both;
.btn {
padding: 14px 59px;
... ...
... ... @@ -27,6 +27,7 @@ shelljs.ls(path.join(__dirname, '/js/**/*.page.js')).forEach((f) => {
'yoho-jquery-accordion',
'yoho-jquery-nanoscroller',
'yoho-jquery-placeholder',
'yoho-jquery-qrcode',
'yoho-jquery-dotdotdot',
'xss'
];
... ...