業務需求
實現小程序支付功能
注意事項(訂單號重複問題)
1.平臺訂單號
2.微信支付訂單號
分開設計,訂單失敗成功以微信訂單號爲準 更新(回調時驗證)相關產品訂單支付狀態;
環境
1.後端java
2.jdk1.7
前端代碼(小程序)
var app = getApp()
Page({
payAction: function () {
var openid = 'oAJ1N5Vg-UcltfCj6uGXXXXX';//自己openID
wx.request({
url: 'http://XXXXX/aglie/tenAppletPay/v1/orderPay',//調用接口生成 統一下單的參數
data: {
openId: openid,//openid
orderId:'20180717154514968',
type:'1'
//body: "尊貴VIP", //商品信息
//totalfee: 100 //金額
},
method: 'POST', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
header: {
'content-type': 'application/x-www-form-urlencoded'
}, // 設置請求的 header
success: function (res) {
console.log("status=" + res.data.status)
console.log("message=" + res.data.message)
var obj = res.data //得到參數
console.log('微信支付接口信息')
console.log('簽名:' + res.data.paySign)
console.log('隨機串:' + res.data.nonceStr)
console.log('時間戳:' + res.data.timeStamp)
console.log('package:' + res.data.package)
wx.requestPayment({
timeStamp: obj.timeStamp,
nonceStr: obj.nonceStr,
package: obj.package,
signType: 'MD5',
paySign: obj.paySign,
success: function (res) {
console.log('支付成功打印信息' )
console.log('timeStam=' + obj.timeStamp)
console.log('nonceStr=' + obj.nonceStr)
console.log('package=' + obj.package)
console.log('paySign=' + obj.paySign)
console.log('成功')
}, fail: function (e) {
console.log(e)
console.log('失敗')
}
})
},
})
}
})
後端代碼(java)
1. 參數配置
// AppID(小程序ID) 小程序唯一標識 (在微信小程序管理後臺獲取)
private static final String APPID = "wx8a52b60XXXXXXX";
// 小程序的 AppSecret(小程序密鑰) (在微信小程序管理後臺獲取)
private static final String APPSECRET = "d3455f3cfb9685c602b60XXXXXXX";
// 授權(必填) 填寫爲 authorization_code (小程序)
private static final String GRANT_TYPE = "authorization_code";
//微信支付的商戶id
public static final String MCHID = "12760XXXXXXX";
// 商戶支付密鑰Key。審覈通過後,在微信發送的郵件中查看
public static final String KEY = "2b60XXXXXXX12345";
//簽名方式,固定值
public static final String SIGNTYPE = "MD5";
//交易類型,小程序支付的固定值爲JSAPI
public static final String TRADETYPE = "JSAPI";
//微信統一下單接口地址
public static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//支付成功後的服務器回調url,Controller裏的回調函數地址
public static final String NOTIFY_URL = "http://XXXX/aglie/tenAppletPay/v1/notify";
2.統一下單支付
/**
*
* @Title: orderPay
* @Description: TODO(統一下單)
* @param: @param request
* @param: @param response
* @param: @param orderId
* @param: @param openId
* @param: @param type 業務類型 {1:諮詢(收費&免費) 4.電商
* @param: @return
* @param: @throws Exception
* @return: Map<String,Object>
* @throws
*/
@ResponseBody
@RequestMapping(value = "/v1/orderPay" , method = RequestMethod.POST)
public Map<String, Object> orderPay(HttpServletRequest request,HttpServletResponse response
,@RequestParam(required=true) String orderId
,@RequestParam(required=true) String openId
,@RequestParam(required=true) String type) throws Exception{
logger.info("微信統一下單 接口調用");
Map<String, Object> map = Maps.newHashMap();
//code獲取參數
if (StringUtils.isBlank(orderId)) {
map.put("status", "fail");
map.put("message", "orderId is not null.");
return map;
}
//這裏根據orderId獲取下單信息 (openId、
logger.info("【orderPay】-接收商品orderId號碼="+orderId + " 業務類型type ="+ type);
//局部變量
String body="尊貴VIP";//訂單 商品信息
int totalfee=0; //訂單 商品金額 微信是按分處理的
String wx_out_trade_no=""; //微信支付訂單號,只能使用一次,非產品訂單號,業務不同前綴不同(規則 諮詢(ZX...) 電商(DS...)
switch (type) {
case "1":
System.out.println("========諮詢微信統一下單處理 開始=========");
//微信支付訂單號生成
Date data = new Date();
wx_out_trade_no="ZX" + DateUtil.dateFormatSSS.format(data)+UtilDate.getThree();
//更新微信支付單號(根據自己業務場景處理)
try {
logger.info("2-- 微信統一下單 訂單號更新訂單庫中");
update_orderIdWx_date(type,orderId,wx_out_trade_no);
} catch (Exception e) {
logger.info("ERROR-更新微信支付訂單號:orderId號碼="+orderId + " 業務類型type ="+ type +" orderIdWx="+wx_out_trade_no);
}
//根據訂單號獲取訂單詳情(根據自己業務場景處理)
logger.info("3-- 微信統一下單 根據orderId 查詢訂單庫商品信息");
MyhQuestion myhQuestionInfo =myhQuestionService.findByOrderId(orderId);
if(myhQuestionInfo!=null){
body="諮詢服務";
//Double不丟失精度,要用BigDecimal處理
BigDecimal v1 = myhQuestionInfo.getFinalPrice();
BigDecimal v2 = new BigDecimal("100");
Double b = v1.multiply(v2).doubleValue();
int fee1 = b.intValue();
totalfee=fee1;
}else {
logger.info("ERROR-查詢訂單信息:orderId號碼="+orderId + " 業務類型type ="+ type);
}
System.out.println("========諮詢微信統一下單處理 結束=========");
break;
default:
break;
}
try {
//獲取客戶端請求code
String appid = AppletConfig.getAppid(); //微信小程序--》“開發者ID”
String mch_id = AppletConfig.getMchid(); //商戶號,將該值賦值給partner
String key = AppletConfig.getKey(); //微信支付商戶平臺登錄)
String openid = openId; //openid
String ip = WXUtil.getIpAddr(request); //ip地址
String body_info = body; //描述
int total_fee = totalfee; //支付金額
String notify_url = AppletConfig.getNotifyUrl(); //回調鏈接
//orderId庫裏訂單ID 與 微信out_trade_no不同;避免支付失敗,微信平臺訂單號出現重複,這裏必須重新生成
String out_trade_no = wx_out_trade_no; //只能使用一次,所以不能使用orderId
Map<Object, Object> map_pay=Applet.weixinPlay(mch_id, appid, key, openid, total_fee, out_trade_no, notify_url, body_info, ip);
if ("SUCCESS".equals(map_pay.get("return_code"))) {
map.put("paySign", map_pay.get("paySign"));
map.put("timeStamp", map_pay.get("timeStamp"));
map.put("nonceStr", map_pay.get("nonceStr"));
map.put("package", map_pay.get("package"));
map.put("status", "success");
map.put("message", "生成訂單");
map.put("return_code", "SUCCESS");
}else {
logger.info("return_code="+map.get("return_code"));
map.put("status", "fail");
map.put("message", "生成訂單失敗");
map.put("return_code", "Fail");
map.put("return_msg", map_pay.get("return_msg"));
}
return map;
} catch (Exception e) {
logger.debug("Error-e:",e);
logger.info("生成統一下單失敗:openId="+openId);
map.put("status", "fail");
map.put("message", "請求失敗");
return map;
}
}
3.回調更新狀態
/**
* 回調函數
* @param request
* @param response
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "v1/notify" , method = RequestMethod.POST)
public void notify(HttpServletRequest request,HttpServletResponse response) throws Exception{
logger.info("支付成功的回調函數(notify)接口調用");
weixinpay_notify(request,response);
}
4.其它方法
weixinPlay()生成微信訂單
/**
* 生成微信訂單
*
* @param openid
* @param period_number
* @param merchant_id
* @param num
* @return
*/
public static SortedMap<Object, Object> weixinPlay(String mch_id,String appid,String key,String openid,int total_fee,String out_trade_no,String notify_url,String body,String ip) throws UnsupportedEncodingException, DocumentException {
logger.info("生成微信訂單(weixinPlay)接口調用");
SortedMap<Object, Object> paymentPo = new TreeMap<Object, Object>();
paymentPo.put("appid",appid);
paymentPo.put("mch_id", mch_id);
paymentPo.put("nonce_str", WXUtil.generate());
paymentPo.put("body",body);
paymentPo.put("out_trade_no",out_trade_no);
paymentPo.put("total_fee",String.valueOf(total_fee));
paymentPo.put("spbill_create_ip", ip);//這裏填你的ip地址
paymentPo.put("notify_url", notify_url);
paymentPo.put("trade_type", AppletConfig.getTradetype()); //JSAPI
paymentPo.put("openid", openid);
//第一次簽名
String sign = WXUtil.createSign_ChooseWXPay("UTF-8", paymentPo, key);
paymentPo.put("sign", sign);
String param = WXUtil.getRequestXml(paymentPo);;
String request = WXUtil.httpRequest(AppletConfig.PAY_URL, "POST", param);
Map<String, String> map = new HashMap<String, String>(); // 將解析結果存儲在HashMap中
InputStream in = new ByteArrayInputStream(request.getBytes());
SAXReader reader = new SAXReader(); // 讀取輸入流
Document document = reader.read(in);
Element root = document.getRootElement(); // 得到xml根元素
@SuppressWarnings("unchecked") // 得到根元素的所有子節點
List<Element> elementList = root.elements();
for (Element element : elementList) {
map.put(element.getName(), element.getText());
}
SortedMap<Object, Object> result = new TreeMap<Object, Object>();
if (map.get("return_code").equals("SUCCESS")) { // 業務結果
String nonceStr =WXUtil.generate();
Long timeStamp = System.currentTimeMillis() / 1000;
SortedMap<Object, Object> params = new TreeMap<Object, Object>();
params.put("appId", appid);
params.put("nonceStr", nonceStr);
params.put("package", "prepay_id=" + map.get("prepay_id"));
params.put("signType", AppletConfig.getSigntype()); //MD5
params.put("timeStamp", timeStamp);
//二次簽名 這個簽名用於小程序端調用wx.requesetPayment方法
String paySign = WXUtil.createSign_ChooseWXPay("UTF-8", params, key);
result.put("paySign", paySign);
result.put("timeStamp", timeStamp + "");
result.put("nonceStr", nonceStr);
result.put("package", "prepay_id=" + map.get("prepay_id"));
result.put("return_code", "SUCCESS");
}else {
result.put("return_code", "Fail");
result.put("return_msg", map.get("return_msg"));
}
return result;
}
weixinpay_notify() 回調方
/**
* 支付成功的回調函數
* @param request
* @param response
* @throws Exception
*/
public void weixinpay_notify(HttpServletRequest request,HttpServletResponse response) throws Exception{
System.out.println("=========支付成功,回調函數(weixinpay_notify)接口調用 開始=========");
InputStream inputStream ;
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String s ;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null){
sb.append(s);
}
in.close();
inputStream.close();
//sb爲微信返回的xml
String notityXml = sb.toString();
Map<String, String> m = new HashMap<String, String>();
m = WXUtil.doXMLParse(sb.toString());
SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
Iterator it = m.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = m.get(parameter);
String v = "";
if(null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
//logger.info("接收到的報文2- packageParams:" + packageParams);
String key = AppletConfig.getKey(); //祕鑰
String resXml = "";
//驗證簽名
if(WXUtil.isTenpaySign("UTF-8", packageParams,key)) {
//logger.info("接收到的報文3- packageParams:" + packageParams);
System.out.println("-------1- 簽名驗證成功--------");
//驗證報文
if("SUCCESS".equals((String)packageParams.get("return_code"))){
// //logger.info("SUCCESS=packageParams:return_code" );
System.out.println("-------2- 報文驗證成功--------");
//得到返回的參數
String openid = (String)packageParams.get("openid");
String transaction_id = (String)packageParams.get("transaction_id");
String out_trade_no = (String)packageParams.get("out_trade_no");
String total_fee = (String)packageParams.get("total_fee");
Float fee= Float.parseFloat(total_fee)/100;
/**此處添加自己的業務邏輯代碼start**/
System.out.println("******處理業務邏輯 開始******");
這裏處理業務
System.out.println("******處理業務邏輯 結束******");
/**此處添加自己的業務邏輯代碼end**/
//通知微信服務器已經支付成功
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
System.out.println("-------3- 通知微信已支付成功--------");
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} else {
System.out.println("----回調失敗,報文爲空----");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[報文爲空]]></return_msg>" + "</xml> ";
}
} else{
System.out.println("----回調失敗,簽名失敗----");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[簽名失敗]]></return_msg>" + "</xml> ";
}
System.out.println("=========支付成功,回調函數(weixinpay_notify)接口調用 結束=========");
}
相關工具類(簽名、祕鑰生成等)這裏就不寫了