Commit a0041802 authored by gaodapeng's avatar gaodapeng

增加支付模块

parent cec1724d
......@@ -4,3 +4,9 @@ export const API_LOGIN = "store/cashier/login";
export const API_SCAN = "market/barcodeQuery";
//核销券
export const API_USE_COUPON = "coupon/use";
//创建订单
export const API_CREATE_ORDER = "order/create";
//支付接口
export const API_PAY = "pay/micropay";
//查询支付结果
export const API_QUERY = "order/query";
\ No newline at end of file
......@@ -17,11 +17,11 @@
<view style="font-size:38rpx;width:300rpx;color:rgba(0,0,0,0.85);font-weight:500;font-family:PingFangSC-Medium,PingFang SC;opacity:0.85;margin:75rpx auto;">请选择支付方式</view>
<view style="width:750rpx;margin-top:78rpx;justify-content:space-between;display:flex;flex-direction:row;box-sizing:border-box;padding-left:96rpx;padding-right:96rpx;">
<view class="payWayContainer">
<view class="payWayContainer" onTap="startPay">
<image mode="aspectFit" src="/images/icon_facepay.png" style="width:134rpx;height:131rpx;margin:10rpx 10rpx 10rpx 10rpx;"/>
<view class="payWayText">刷脸支付</view>
</view>
<view class="payWayContainer">
<view class="payWayContainer" onTap="startPay">
<image mode="aspectFit" src="/images/icon_qrpay.png" style="width:134rpx;height:131rpx;margin:10rpx 10rpx 10rpx 10rpx;"/>
<view class="payWayText">扫码支付</view>
</view>
......
var payUtils = require("../../utils/payUtils.js");
Page({
data: {
goodsNum:0,
......@@ -5,6 +7,10 @@ Page({
totalPrice:0.00,
couponPrice:0.00,
qrPayShow:false,
list:[],
orderNo:"",
isCashierClosed:false,
isPaying: false,
},
onLoad(options) {
var totalQuantity = options.totalQuantity
......@@ -27,16 +33,130 @@ Page({
},
facePay(){
/**
* @todo 支付结果的获取
* @todo 跳转数据的保存
*/
startPay(){
//开始loading
my.showLoading({});
},
//获取order
payUtils.getOrder(/*this.data.payPrice*/1,this.data.list, function(res){
if(res != null){
qrPay(){
//todo 这里增加唤起弹窗的逻辑
console.log("callback调用,res="+JSON.stringify(res));
if(res.resultCode ==1){
this.setData({
qrPayShow:true
orderNo:res.data
});
this.payWithOpenCashier();
}
}
}.bind(this));
},
//调用收款接口
doPay(orderNo, payCode) {
console.log("doPay调用");
this.setData({isPaying:true});
//使用barCode来当面付
payUtils.startPay(orderNo, payCode, 1, this.data.list, function(res){
my.hideLoading();
console.log("支付回调 ===>" + JSON.stringify(res));
this.setData({isPaying:false});
if(res == null){
return;
}
if(res.code > 0){
//成功
console.log("支付回调成功,跳转 " );
// if(data.isCashierClosed == true){
my.ix.offCashierEventReceive();
my.redirectTo({
url: '/pages/paysuccess/paysuccess?orderPrice=' + this.data.totalPrice + "&couponPrice=" + this.data.couponPrice + "&payPrice=" + this.data.payPrice+"&totalCount="+this.data.totalCount,
});
// }
}else{
console.log("支付回调失败,toast提示 ");
my.ix.offCashierEventReceive();
my.showToast({
type: 'fail',
content: res.data,
duration: 2000
});
}
}.bind(this));
},
/**
* 打开收银台,进行扫码收款
*/
payWithOpenCashier(orderNo){
try{
console.info("收银台版本" + my.ix.getVersionSync({
packageName: 'zoloz.phone.android.alipay.com.dragonfly'
}).versionName);
//开启收银台监听
my.ix.startApp({
appName: "cashier",
bizNo: this.data.orderNo,
totalAmount: this.data.payPrice,
success: (res) => {
console.log("收银台启动回调 success==>" + JSON.stringify(res));
if (res.codeType == "F") {
this.doPay(orderNo, res.barCode);
} else {
this.data.isCashierClosed = true;
this.doPay(orderNo, res.barCode);
}
},fail:(res) => {
my.showToast({
type: 'exception',
content: api.decodeErrorMessage(res.error),
duration: 2000
});
}
});
//收银台回调
my.ix.onCashierEventReceive((res) => {
console.log("onCashierEventReceive==>" + JSON.stringify(res));
if (res.bizType == 'RESULT_CLOSED' || res.status == "RESULT_CLOSE") {
console.log("收银台关闭");
this.data.isCashierClosed = true;
//关闭监听
my.ix.offCashierEventReceive();
if (this.data.isPaying == true) {
//如果正在支付中,则等待支付结果返回
} else {
//跳转支付结果页面,说明支付已完成,无论取消支付还是支付成功等都跳转支付结果页
// if (this.data.actualPayPriceYuan != null) {
// my.redirectTo({
// //payPrice和couponPrice需要考虑支付宝优惠
// url: '/pages/paysuccess/paysuccess?orderPrice=' + this.data.totalPrice + "&couponPrice=" + this.data.couponPrice + "&payPrice=" + this.data.payPrice+"&totalCount="+this.data.totalCount,
// });
// } else {
// //说明用户取消了支付
// my.redirectTo({
// url: '/pages/paysuccess/paysuccess?payFailedMsg=' + "用户取消" + "&payPriceYuan=" + this.data.payPriceYuan,
// });
// }
}
} else if (res.bizType == 'RESULT_BTN_FUNCTION')
console.log("收银台自定义按钮按下");
else if (res.bizType == 'RESULT_MEMBER')
console.log("支付结果页会员开卡");
else
console.log('RESULT: ' + res.bizType);
});
} catch {
console.log("收银台启动回调 异常==>" + JSON.stringify(err));
}
},
});
var api = require("/utils/api.js");
import{API_CREATE_ORDER} from '/const/apiurl'
import{API_PAY} from '/const/apiurl'
import{API_QUERY} from'/const/apiurl'
/**
* tradeAmount int
* list:
* name string
* price int
* salePrice int
* quantity int
* barcode string
* callback,方法,参数为一对象,对象内属性如下:
* resultCode: int 成功:1/失败:-1
* data:如果success == false,data 为String提示。否则为数据体。包含数据有:
*
*/
function getOrder(
tradeAmount,
list,//物品列表
callback
){
if(checkGoodsList(list) != true){
console.log("物品列表不是数组");
callback({resultCode:-1,message:"物品列表不是数组"});
return
}
var app = getApp();
var orderCreateRequest = api.createCommonRequest();
if (app.globalData.localAccountInfo != null) {
orderCreateRequest.storeId = app.globalData.localAccountInfo.storeId;
}
//测试代码
orderCreateRequest.storeId = "001";
orderCreateRequest.tradeAmount = tradeAmount;
orderCreateRequest.deviceType = "ALIPAY";
orderCreateRequest.list = list;
orderCreateRequest.batchNo = "" + Date.parse(new Date());
console.log("请求创建订单 ==>" +JSON.stringify(orderCreateRequest));
var orderUrl = getApp().globalData.BASE_URL + API_CREATE_ORDER;
console.log("createOrderUrl"+orderUrl);
my.request({
url: orderUrl,
method: 'POST',
dataType: 'json',
timeout: 30000,
data: orderCreateRequest,
success: (res) => {
//判断返回值
if (res.data.success != true) {
console.log("创建订单接口返回失败 ==>" + JSON.stringify(res.data));
callback({resultCode:-1,message:"获取订单号失败"});
} else {
if(res.data.data != null){//返回值有效
var response = {resultCode:1,message:"获取订单号成功",data:res.data.data};
callback(response);
}
console.log("创建订单成功 ==>" + JSON.stringify(res));
}
},
fail: (res) => {
console.log("创建订单接口调用失败 ==>" + JSON.stringify(res)+"\terror:"+res.error);
my.showToast({
type: 'exception',
content: api.decodeErrorMessage(res.error),
duration: 2000
});
callback({resultCode:-1,message:"获取订单号失败"});
}
});
}
/**
* 对输入数据做一次安全检查
* name string
* price int
* salePrice int
* quantity int
* barcode string
*
*/
function checkGoodsList(list){
if((list instanceof Array)!==true){
console.log("数组判断有效");
return false;
}
if(list.length >0){
var tempGoods;
for(var i=0;i<list.length;i++){
tempGoods = list[i];
if(tempGoods == null){
continue;
}
if(tempGoods.price == 0||tempGoods.salePrice == 0){
return false;
}
}
}
return true;
}
/**
* 转换goods的bean到支付接口使用
* goodsId
* goodsName
* price
* quantity
* activityId
*/
function transGoodsList(originList){
var newList = [];
var tempOldGoods;
var tempNewGoods;
for(i = 0; i < originList.length; i++) {
tempOldGoods = originList[i];
if(tempOldGoods == null){
continue;
}
tempNewGoods = {};
tempNewGoods.goodsId = tempOldGoods.barcode;
tempNewGoods.goodsName = tempOldGoods.name;
tempNewGoods.price = tempOldGoods.price;
tempNewGoods.quantity = tempOldGoods.quantity;
newList.push(tempNewGoods);
}
return newList;
}
/**
* res:
* (要注意以下是否是大些的字段)
* storeId
* tradeNo
* discount
* mdiscount
* tradeAmount
* tradeTime
* channel
* userId 买家用户id
* merchantId 商户id
// * operatorId 操作员id
*/
function onPaySuccess(res, callback){
var response = {resultCode:1,message:"支付成功",data:{}};
//todo 返回正确数据到调用方
response.data = res;
//或者可以解析出来res的内容,然后重组发给外界
callback(response);
}
function startPay(tradeNo, payCode, payPrice, list, callback){
pay(tradeNo,payCode, payPrice, list, callback,0);
}
/**
* @todo 写支付
* 支付参数
* tradeNo
channel 非必须
payAction
subChannel 非必须
invoice 非必须
totalFee
authCode
goodsDetail
goodsId
goodsName
price
quantity
activityId
*/
function pay(tradeNo, payCode, payPrice, list, callback, retryCount){
var payRequest = api.createCommonRequest();
payRequest.tradeNo = tradeNo;
payRequest.channel = "alipay";
payRequest.payAction = "create";
payRequest.subChannel = "JSAPI";
payRequest.invoice = "35";//可能不是
payRequest.totalFee = payPrice;//传string即可
payRequest.goodsDetail = list;
payRequest.authCode = payCode;
payRequest.batchNo = "" + Date.parse(new Date());
var payUrl = getApp().globalData.BASE_URL + API_PAY;
console.log("pay ===> url:"+payUrl+"\t body"+JSON.stringify(payRequest));
my.request({
url: payUrl,
method: 'POST',
dataType: 'json',
timeout: 30000,
data: payRequest,
success: (res) => {
//增加数据判断,增加重试
if (res == null|| res.data == null|| res.data.success != true||res.data.data == null) {
console.log("支付未成功 ==>" + res.data.message);
retryOrFailed(tradeNo, payCode, payPrice, list, callback, retryCount);
} else {
console.log("支付返回值 ==>" + JSON.stringify(res.data));
if(res.data.data.tradeStatus == "3"){
onPaySuccess(res.data.data,callback);
} else if(res.data.data.tradeStatus == "1"){//需要查询结果
//轮询查询,最多12次
queryPayStateWithRetry(tradeNo,callback,0);
} else {
//查询一次
queryPayState(tradeNo,callback);
}
}
},
fail: (res) => {
console.log("支付失败 ==>" + JSON.stringify(res));
retryOrFailed(tradeNo, payCode, payPrice, list, callback, retryCount);
}
});
}
function sleep(numberMillis) {
var now = new Date();
var exitTime = now.getTime() + numberMillis;
while (true) {
now = new Date();
if (now.getTime() > exitTime)
return;
}
}
function retryOrFailed(tradeNo, payCode, payPrice, list, callback, retryCount){
if(retryCount < 12){
sleep(5000);
console.log("重试中...重试次数: ==>" + (retryCount+1));
pay(tradeNo, payCode, payPrice, list, callback, ++retryCount);
} else {
console.log("支付接口重试超限,支付失败");
var response = {resultCode:-1,message:"支付失败"};
callback(response);
}
}
/**
* 带重试的查询
*/
function queryPayStateWithRetry(tradeNo, callback, retryCount){
if(retryCount >= 12){
console.log("支付接口重试超限,支付失败");
var response = {resultCode:-1,message:"支付失败"};
callback(response);
return;
}
retryCount++;
var queryStateRequest = api.createCommonRequest();
queryStateRequest.tradeNo = tradeNo;
queryStateRequest.batchNo = "" + Date.parse(new Date());
var queryUrl = getApp().globalData.BASE_URL + API_QUERY;
console.log("开始查询状态==> url:"+queryUrl+"\trequest:"+JSON.stringify(queryStateRequest));
my.request({
url: queryUrl,
method: 'POST',
dataType: 'json',
timeout: 30000,
data: queryStateRequest,
success: (res) => {
//增加数据判断,增加重试
if (res == null|| res.data == null|| res.data.success != true||res.data.data == null) {//不知名原因导致失败,直接报失败
console.log("查询失败,支付失败");
var response = {resultCode:-1,message:"支付失败"};
callback(response);
} else {
if(res.data.data.tradeStatus == "3"){//成功直接报成功
onPaySuccess(res.data.data,callback);
} else if(res.data.data.tradeStatus == "1"){//需要查询结果,继续轮询
console.log("查询结果显示支付未成功 ==>" + res.data.message);
sleep(5000);
queryPayStateWithRetry(tradeNo,callback, retryCount);
} else {//其他状况直接报失败
var response = {resultCode:-1,message:"支付失败"};
callback(response);
}
}
},
fail: (res) => {//接口访问失败,继续轮询
console.log("查询失败 ==>" + JSON.stringify(res));
sleep(5000);
queryPayStateWithRetry(tradeNo,callback,retryCount);
}
});
}
/**
* 不带重试的查询
*/
function queryPayState(tradeNo, callback){
var queryStateRequest = api.createCommonRequest();
queryStateRequest.tradeNo = tradeNo;
queryStateRequest.batchNo = "" + Date.parse(new Date());
var queryUrl = getApp().globalData.BASE_URL + API_QUERY;
console.log("开始查询状态==> url:"+queryUrl+"\trequest:"+JSON.stringify(queryStateRequest));
my.request({
url: queryUrl,
method: 'POST',
dataType: 'json',
timeout: 30000,
data: queryStateRequest,
success: (res) => {
//增加数据判断,增加重试
if (res == null|| res.data == null|| res.data.success != true||res.data.data == null) {//不知名原因导致失败,直接报失败
console.log("查询失败,支付失败");
var response = {resultCode:-1,message:"支付失败"};
callback(response);
} else {
console.log("查询结果 ==>" + JSON.stringify(res.data));
if(res.data.data.tradeStatus == "3"){//成功直接报成功
onPaySuccess(res.data.data,callback);
} else {//其他状况直接报失败
var response = {resultCode:-1,message:"支付失败"};
callback(response);
}
}
},
fail: (res) => {//接口访问失败,继续轮询
console.log("查询失败 ==>" + JSON.stringify(res));
var response = {resultCode:-1, message:"支付失败"};
callback(response);
}
});
}
module.exports.startPay = startPay;
module.exports.getOrder = getOrder;
\ No newline at end of file
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