微信小程式支付功能,完成整個交易的思路和程式碼
阿新 • • 發佈:2018-12-19
開發工具:微信開發者工具,Intellij idea 2018
框架:spring boot
交易流程圖:
下面直接上程式碼:
1.在wxml新增一個支付按鈕,點選監聽payMethod方法
<view><button bindtap='payMethod'>支付</button></view>
2.payMethod方法種呼叫wx.login微信介面,獲取code。呼叫getOpenId方法
/** * 支付例子 * const config = require('../../config')獲取全域性配置 * const appInstance = getApp();獲取全域性上下文例項 */ payMethod(){ let _this = this ;//當前上下文 //獲取登入code wx.login({ success: result =>{ console.info(result.code) //獲取openid _this.getOpenId(result.code) } }); },
3.將code作為引數,請求Java服務,獲取openId
/** * 用code作為引數請求Java服務,像微信服務換取openId,access_token * 此處用到openId,access_token暫時無用,為安全起見,此欄位不返回客戶端 */ getOpenId(code){ let _this = this;//當前上下文 wx.request({ url: config.localServer + 'api/wc/jscode2session.wc', data: { code: code}, method: 'POST', success: result =>{ console.info('返回openId') console.info(result.data) _this.generateOrder(result.data.data.openid) }, fail:() => { console.info('fail') }, complete: () => { // complete } }) },
4.Java服務jscode2session介面,呼叫微信sns/jscode2session服務,獲取openId,將openid返回給小程式客戶端
/** * 換取 使用者唯一標識 OpenID 和 會話金鑰 session_key ->>> oopenid 和 session_key 組成會話標識,發給客戶端,客戶端憑藉這個標識進行通訊 * 會話金鑰 session_key 是對使用者資料進行 加密簽名 的金鑰。 * 為了應用自身的資料安全,開發者伺服器不應該把會話金鑰下發到小程式,也不應該對外提供這個金鑰。 * 臨時登入憑證 code 只能使用一次 * @param request * @param requestEntity * @param code * @return */ @PostMapping("/jscode2session.wc") public ResponseBean jscode2session(HttpServletRequest request, HttpEntity<String> requestEntity,String code) { HttpMethod requestMethod = HttpMethod.resolve(request.getMethod()); String body = requestEntity.getBody(); Map<String,String> mapParam = JacksonUtil.fromJson(body,Map.class); code = mapParam.get("code"); RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8); requestEntity = new HttpEntity<>(requestEntity.getBody(), headers); ResponseEntity<String> response = null; response = restTemplate.exchange( wcHost+"sns/jscode2session?appid=wxd0ad6e09b4f09a2a&secret=919f06b91b6adbe682c0bd9edc0a008f&js_code="+code+"&grant_type=authorization_code", requestMethod, requestEntity, String.class); String result = response.getBody(); LOGGER.info("result="+result); Map<String,String> mapResult = JacksonUtil.fromJson(result,Map.class); return ResponseBean.response(mapResult); }
5.小程式獲取openid,呼叫generateOrder方法,生成訂單資訊,呼叫Java服務payOrderPublic.wc介面,以獲取支付引數
/**
* 用openid,在Java服務請求微信服務,獲取支付的請求引數
*/
generateOrder(openid){
var _this = this
var paymentPo={
openid: openid,
total_fee: '0.1',
mch_id: 'mch_id',
body: '支付測試',
detail: 'detail',
attach: '假酒'
}
wx.request({
url: config.localServer + 'api/wc/payOrderPublic.wc',
method: 'POST',
data: paymentPo,
success: result => {
console.info(result)
var pay = result.data.data
//發起支付
var timeStamp = pay[0].timeStamp;
console.info("timeStamp:" + timeStamp)
var packages = pay[0].package;
console.info("package:" + packages)
var paySign = pay[0].paySign;
console.info("paySign:" + paySign)
var nonceStr = pay[0].nonceStr;
console.info("nonceStr:" + nonceStr)
var param = { "timeStamp": timeStamp, "package": packages, "paySign": paySign, "signType": "MD5", "nonceStr": nonceStr };
_this.pay(param)
},
})
},
6.Java服務payOrderPublic.wc介面,將引數生成簽名,打包成xml格式,請求微信的統一下單介面/pay/unifiedorder,將獲取的結果進行再次簽名,得到支付介面需要的引數,返回給小程式客戶端。
/**
* 商戶server呼叫支付統一下單
// * @param openid 使用者唯一標識
// * @param mch_id 商品編號
// * @param total_fee 商品價格
// * @param body 商品描述
// * @param detail 商品詳情
// * @param attach 附加資料
// * @param time_start 交易開始時間
// * @param time_expire 交易結束時間
* @return
*/
@PostMapping("/payOrderPublic.wc")
public ResponseBean payOrderPublic(HttpServletRequest request,HttpEntity<String> requestEntity,PaymentPo paymentPo) throws UnsupportedEncodingException, DocumentException {
paymentPo = JacksonUtil.fromJson(requestEntity.getBody(),PaymentPo.class);
paymentPo.setBody(new String(paymentPo.getBody().getBytes("UTF-8"),"ISO-8859-1")) ;
// String appid = "替換為自己的小程式ID";//小程式ID
//當前時間
String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
//生成8位隨機數
String code = PayUtil.createCode(8);
//商戶訂單號
String out_trade_no = paymentPo.getMch_id()+today+code;//商戶訂單號
String spbill_create_ip = HttpUtil.getIpAddress(request);//終端IP
String notify_url = "http://www.weixin.qq.com/wxpay/pay.php";//通知地址
String trade_type = "JSAPI";//交易型別
paymentPo.setAppid(appid);
//隨機字串 32位
String str=UUIDHexGeneratorUtil.generate();
paymentPo.setNonce_str(str);
paymentPo.setOut_trade_no(out_trade_no);
paymentPo.setSpbill_create_ip(spbill_create_ip);
paymentPo.setNotify_url(notify_url);
paymentPo.setTrade_type(trade_type);
// 把請求引數放進hashmap
Map<String,String> sParaTemp = new HashMap<String,String>();
sParaTemp.put("appid", paymentPo.getAppid());
sParaTemp.put("mch_id", paymentPo.getMch_id());
sParaTemp.put("nonce_str", paymentPo.getNonce_str());
sParaTemp.put("body", paymentPo.getBody());
sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no());
sParaTemp.put("total_fee",paymentPo.getTotal_fee());
sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip());
sParaTemp.put("notify_url",paymentPo.getNotify_url());
sParaTemp.put("trade_type", paymentPo.getTrade_type());
sParaTemp.put("openid", paymentPo.getOpenid());
// 除去map中的空值和簽名引數
Map<String,String> sPara = PayUtil.paraFilter(sParaTemp);
// 把陣列所有元素,按照“引數=引數值”的模式用“&”字元拼接成字串
String prestr = PayUtil.createLinkString(sPara);
String key = "&key=替換為商戶支付金鑰"; // 商戶支付金鑰
//MD5運算生成簽名
String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase();
paymentPo.setSign(mysign);
//打包要傳送的xml
String respXml = MessageUtil.messageToXML(paymentPo);
// 列印respXml發現,得到的xml中有“__”不對,應該替換成“_”
respXml = respXml.replace("__", "_");
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//統一下單API介面連結
String param = respXml;
String result =PayUtil.httpRequest(url, "POST", param);
// 將解析結果儲存在HashMap中
Map<String,String> map = new HashMap<String,String>();
InputStream in = new ByteArrayInputStream(result.getBytes());
// 讀取輸入流
SAXReader reader = new SAXReader();
Document document = reader.read(in);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子節點
@SuppressWarnings("unchecked")
List<Element> elementList = root.elements();
for (Element element : elementList) {
map.put(element.getName(), element.getText());
}
// 返回資訊
String return_code = map.get("return_code");//返回狀態碼
String return_msg = map.get("return_msg");//返回資訊
System.out.println("return_msg"+return_msg);
Map<String,Object> resultMap = new HashMap<String,Object>();
if(return_code=="SUCCESS"||return_code.equals(return_code)){
// 業務結果
String prepay_id = map.get("prepay_id");//返回的預付單資訊
String nonceStr=UUIDHexGeneratorUtil.generate();
resultMap.put("nonceStr", nonceStr);
resultMap.put("package", "prepay_id="+prepay_id);
Long timeStamp= System.currentTimeMillis()/1000;
resultMap.put("timeStamp", timeStamp+"");
String stringSignTemp = "appId="+appid+"&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id+ "&signType=MD5&timeStamp=" + timeStamp;
//再次簽名
String paySign=PayUtil.sign(stringSignTemp, "&key=替換為自己的金鑰", "utf-8").toUpperCase();
resultMap.put("paySign", paySign);
}
return ResponseBean.response(resultMap) ;
}
7.小程式端得到支付引數,用pay方法呼叫微信的支付介面,完成支付
/**
* 支付的動作
*/
pay(param){
console.info("支付")
console.info(param)
wx.requestPayment({
timeStamp: param.timeStamp,
nonceStr: param.nonceStr,
package: param.package,
signType: param.signType,
paySign: param.paySign,
success: (result) => {
console.info("支付")
console.info(result)
},
fail: (result) => {
console.info("支付失敗")
console.info(result)
},
complete: () => {
console.info("pay complete")
}
})
}