申請微信支付能力
* 要想使用微信支付能力,不管是App支付、公眾號支付、h5支付等支付方式都需要先在微信商戶平臺申請開通支付能力。
* 申請開通支付能力的資料有公司營業執照、負責人身份證正反面等圖片,相關所需的所有資料在微信官方商戶平臺上有說明。
* 申請完開通支付能力后,我們會得到商戶號以及appId,然后設置32位官方密鑰。
準備工作
* 如果你是h5支付,還需要去微信商戶平臺設置支付URL的IP或者域名,一般最多可以設置5個IP或者域名,建議同時將正式環境和測試環境的IP或者域名設置好。
* 如果你是公眾號支付,同上,你也需要設置你的支付IP或者域名,注意,異步通知的URL也要在你設置的IP或者域名下。
業務流程
- 用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。(app端向服務端發起請求)
- 商戶后臺收到用戶支付單,調用微信支付統一下單接口。(服務端向微信請求)。
- 通過微信支付返回的數據調起微信支付。
- 用戶在微信端支付成功。
- 支付完成后,微信APP會返回到商戶APP并回調onResp函數,開發者需要在該函數中接收通知,判斷返回錯誤碼,如果支付成功則去后臺查詢支付結果再展示用戶實際支付結果。注意 一定不能以客戶端返回作為用戶支付的結果,應以服務器端的接收的支付通知或查詢API返回的結果為準。
- 商戶APP調起微信支付。(app端利用服務端回傳的參數調起微信支付)
- 商戶后臺接收支付通知。(微信將支付結果異步通知服務端)
- 商戶后臺查詢支付結果。(微信將支付結果同步通知app端)
時序圖
注意事項
- 訂單金額total_fee使用的單位是分,支付寶使用的單位是元。
開始集成
*** APP支付**
支付集成流程如下(微信和支付寶流程上基本一致,具體參考代碼):
步驟1:用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。(app端向服務端發起請求)
步驟2:商戶后臺收到用戶支付單,調用微信支付統一下單接口。(服務端向微信請求)
步驟3:統一下單接口返回正常的prepay_id,再按簽名規范重新生成簽名后,將數據傳輸給APP。參與簽名的字段名為appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式為Sign=WXPay(將微信回傳的prepayid與其他參數組合返回給app端)
步驟4:商戶APP調起微信支付。(app端利用服務端回傳的參數調起微信支付)
步驟5:商戶后臺接收支付通知。(微信將支付結果異步通知服務端)
步驟6:商戶后臺查詢支付結果。(微信將支付結果同步通知app端)
微信支付代碼
微信統一下單接口和回調
@Override
public String getWxPayOrderStr(String orderNo, HttpServletRequest request) {
//最終返回加簽之后的,app需要傳給支付寶app的訂單信息字符串 String orderString = "";
System.out.println("==================微信下單,商戶訂單號為:" + orderNo);
String appId = PayConfigUtil.APP_ID;//appId String mchId = PayConfigUtil.MCH_ID;//商戶id String key = PayConfigUtil.API_KEY;
String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
String nonce_str = strTime + strRandom;
order_price = order_price.substring(0, order_price.indexOf("."));
String body = teaNames;
String spbill_create_ip = PayConfigUtil.CREATE_IP;
String notify_url = PayConfigUtil.NOTIFY_URL;
String trade_type = "APP";
SortedMap<Object, Object> map = new TreeMap<>();
map.put("appid", appId);
map.put("mch_id", mchId);
map.put("nonce_str", nonce_str);
map.put("body", body);
map.put("out_trade_no", orderNo);
map.put("total_fee", order_price);
map.put("spbill_create_ip", spbill_create_ip);
map.put("notify_url", notify_url);
map.put("trade_type", trade_type);
//簽名數據,MD5加密 String sign = PayCommonUtil.createSign("UTF-8", map, key);
map.put("sign", sign);
//請求微信支付,進行下單 String requestXML = PayCommonUtil.getRequestXml(map);
String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
System.out.println("微信返回值:" + resXml);
Map<String, String> data = null;
try {
data = XMLUtil.doXMLParse(resXml);
if (data.get("return_code").equals("SUCCESS")) {
//返回給APP端的參數,APP端再調起支付接口 SortedMap<Object, Object> repData = new TreeMap<>();
repData.put("appid", appId);
repData.put("noncetr", nonce_str);
repData.put("partnerid", mchId);
repData.put("prepayid", data.get("prepay_id"));
repData.put("package", "Sign=WXPay");
repData.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
String sign1 = PayCommonUtil.createSign("UTF-8", repData, key);
JSONObject json = new JSONObject();
json.put("appid", appId);
json.put("partnerid", mchId);
json.put("package", "Sign=WXPay");
json.put("noncestr", nonce_str);
json.put("prepayid", data.get("prepay_id"));
json.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
json.put("sign", "1232");
return json.toString();
} else {
return "";
}
} catch (JDOMException | IOException e) {
e.printStackTrace();
return "";
}
}
@PostMapping("/wxPayAppCallback")
public void wxPayCallback(HttpServletRequest request, HttpServletResponse response) throws JDOMException, Exception {
System.out.println("微信支付回調");
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "utf-8");
Map<String, String> map = XMLUtil.doXMLParse(resultxml);
outSteam.close();
inStream.close();
System.out.println("微信返回值:" + map);
String resultCode = map.get("result_code");
//訂單號 String out_trade_no = map.get("out_trade_no");
if (StringUtils.equals(resultCode, "SUCCESS")) {
//業務代碼
System.out.println("微信回調成功");
//返回微信處理成功 response.setContentType("text/xml");
response.getWriter().println("success");
} else {
response.setContentType("text/xml");
response.getWriter().println("fail");
}
}
支付寶支付代碼
支付寶支付和回調
/** * 獲取支付寶加簽后臺的訂單信息字符串 */
@Override
public String getAliPayOrderStr(String orderNo) {
String teaNames = "";//商品名稱
//最終返回加簽之后的,app需要傳給支付寶app的訂單信息字符串 String orderString = "";
System.out.println("==================支付寶下單,商戶訂單號為:" + orderNo);
try {
//實例化客戶端(參數:網關地址、商戶appid、商戶私鑰、格式、編碼、支付寶公鑰、加密類型),為了取得預付訂單信息 AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGN_TYPE);
//實例化具體API對應的request類,類名稱和接口名稱對應,當前調用接口名稱:alipay.trade.app.pay AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest();
//SDK已經封裝掉了公共參數,這里只需要傳入業務參數。以下方法為sdk的model入參方式 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
//業務參數傳入,可以傳很多,參考API //model.setPassbackParams(URLEncoder.encode(request.getBody().toString())); //公用參數(附加數據)
model.setBody("購買商品");//對一筆交易的具體描述信息。如果是多種商品,請將商品描述字符串累加傳給body。
model.setSubject(teaNames);//商品名稱
model.setOutTradeNo(orderNo);//商戶訂單號(自動生成)
model.setTimeoutExpress("15m");//交易超時時間
model.setTotalAmount(String.valueOf(orderTea.getOrderTotal()));//支付金額
model.setProductCode("QUICK_MSECURITY_PAY");//銷售產品碼(固定值)
ali_request.setBizModel(model);
ali_request.setNotifyUrl(AlipayConfig.notify_url);//異步回調地址(后臺)
ali_request.setReturnUrl(AlipayConfig.return_url);//同步回調地址(APP)
// 這里和普通的接口調用不同,使用的是sdkExecute
AlipayTradeAppPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(ali_request); //返回支付寶訂單信息(預處理)
orderString = alipayTradeAppPayResponse.getBody();//就是orderString 可以直接給APP請求,無需再做處理。
} catch (AlipayApiException e) {
e.printStackTrace();
}
return orderString;
}
@PostMapping("/appCallback")
public void appCallback(HttpServletRequest request, HttpServletResponse response) {
try {
// 獲取支付寶POST過來反饋信息 Map<String, String> params = new HashMap<>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
// 亂碼解決,這段代碼在出現亂碼時使用。如果mysign和sign不相等也可以使用這段代碼轉化 valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
// 調用SDK驗證簽名 boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
//先不做回調驗證 //if (signVerified) { System.out.println("回調驗簽");
// 商戶訂單號(后臺生成) String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 支付寶交易號 String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 交易狀態 String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
System.out.println("trade_no:" + trade_no);
// 獲取支付寶的通知返回參數,可參考技術文檔中頁面跳轉同步通知參數列表(以上僅供參考)// if (StringUtils.equals("TRADE_SUCCESS", trade_status)) {
//ZK表示用戶購買了賺客商品開通了賺客 if (out_trade_no.contains("d") || out_trade_no.contains("ZK")) {
String[] orderNoArr = new String[1024];
int a = 0;
//根據訂單號修改訂單狀態 MallOrder mallOrder = new MallOrder();
mallOrder.setOrderStatus(Byte.valueOf("1"));
mallOrder.setOutTradeNo(trade_no);//支付寶交易號 mallOrder.setPayChannel(Byte.valueOf("2"));//支付方式 mallOrder.setPayTime(new Date());
mallOrder.setCreated(true);
if (out_trade_no.contains(",")) {
orderNoArr = out_trade_no.split(",");
for (String orderNo : orderNoArr) {
MallOrderExample mallOrderExample = new MallOrderExample();
mallOrderExample.createCriteria().andOrderNoEqualTo(orderNo);
saveRecord(orderNo);
a += orderService.updateByExampleSelective(mallOrder, mallOrderExample);
}
} else {
saveRecord(out_trade_no);
MallOrderExample mallOrderExample = new MallOrderExample();
mallOrderExample.createCriteria().andOrderNoEqualTo(out_trade_no);
a = orderService.updateByExampleSelective(mallOrder, mallOrderExample);
}
if (a > 1) {
System.out.println("修改成功");
}
System.out.println("訂單提交成功");
} else {
//根據訂單號修改訂單狀態,生成取貨碼 machineOrderTea orderTea = machineService.findMachineOrder(out_trade_no);
String prefix = RandomStringUtils.random(4, "1234567890").toUpperCase();
long l = System.currentTimeMillis();
String newSuffix=String.valueOf(l).substring(String.valueOf(l).length()-5,String.valueOf(l).length());
String suffix = String.valueOf(l / 500000000);
// String verificationCode = prefix + suffix; String verificationCode = prefix + newSuffix;
orderTea.setVerificationCode(verificationCode);
orderTea.setTradeNo(trade_no);
orderTea.setOrderNo(out_trade_no);
orderTea.setPayMethod("Alipay");
orderTea.setOrderStatus("1");
//如果使用了優惠券則將優惠券設置為已使用 Integer couponLinkId = orderTea.getCouponId();
if (null != couponLinkId) {
MemberCouponsLink link = couponsLinkMapper.selectByPrimaryKey(couponLinkId);
//設置優惠為已使用// link.setIsUse(true); /*臨時方案↓*/ //如果優惠券碼不為"888888"或者"666666",則設置優惠券為"已使用" if(!(link.getCouponCode().equals("888888")||link.getCouponCode().equals("666666"))){
link.setIsUse(true);
}
/*臨時方案↑*/ couponsLinkMapper.updateByPrimaryKeySelective(link);
}
machineService.updateMachineOrder(orderTea);
System.out.println("訂單提交成功");
}
String result = "success";
// if(signVerified){//驗簽成功// result="success";// }else{// result="fail";// } //,不然業務代碼不會執行 BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(result.getBytes());
out.flush();
out.close();
}
// } else {// System.out.println("回調驗簽失敗");// } } catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("支付異常");
}
}