微信支付公的眾號(hào)支付和掃碼支付
來(lái)源:程序員人生 發(fā)布時(shí)間:2016-07-07 09:00:55 閱讀次數(shù):3231次
公眾號(hào)支付是手機(jī)真?zhèn)€微信公眾號(hào)H5頁(yè)面支付,這類(lèi)支付方式必須是在微信內(nèi)置閱讀器發(fā)起。
掃碼支付分為模式1和模式2,模式1主要為線下服務(wù),該模式是先掃碼,再生成定單,商戶(hù)先為自己的商品生成2維碼連接,然后用戶(hù)掃碼以后決定是不是購(gòu)買(mǎi),2維碼無(wú)過(guò)期時(shí)間,比如自動(dòng)售賣(mài)機(jī)大多采取這類(lèi)模式;模式2主要為線上電商服務(wù),用戶(hù)選擇商品后生成定單,根據(jù)定單生成2維碼,然后支付,該2維碼為臨時(shí)2維碼。
開(kāi)發(fā)流程
1、授權(quán)目錄
官方文檔說(shuō)必須是精確目錄,實(shí)際上是2級(jí)或3級(jí)目錄就能夠了,太精確的可能還會(huì)出現(xiàn)不辨認(rèn)的情況。如果是掃碼支付模式1還需要設(shè)置掃碼支付回調(diào)URL

2.統(tǒng)1下單
注意傳入?yún)?shù)不要為null,盡可能不要是空字符串,如果在沒(méi)有拋出Exception的情況下支付失敗,10有89是參數(shù)致使的簽名有問(wèn)題,微信支付的簽名規(guī)定的特別嚴(yán)格,必須依照微信給的規(guī)則來(lái),建議第1次先用demo提供的簽名方法,以后可以再修改。這里的appid就是支付所在的公眾號(hào)的appid,openId是用戶(hù)對(duì)應(yīng)當(dāng)前公眾號(hào)的openId。
1、掃碼支付模式2
掃碼支付比較簡(jiǎn)單,可以直接通過(guò)url發(fā)起,傳入統(tǒng)1下單參數(shù),生成掃碼支付的url。掃碼支付的trade_type為NATIVE。
public void getCodeUrl(@PathVariable(value="tradeNo")String tradeNo, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//根據(jù)定單號(hào)獲得定單詳情
OrderProduceBean order = reservationCarService.getTradebyNo(tradeNo);
// 附加數(shù)據(jù) 原樣返回
String attach = "attach";
// 總金額以分為單位,不帶小數(shù)點(diǎn)
String totalFee = TenpayUtil.getMoney(order.getTotalFee().toString());
// 定單生成的機(jī)器 IP
String spbill_create_ip = IpUtil.getIpAddr(request);
// 這里notify_url是 支付完成后微信發(fā)給該鏈接信息,可以判斷會(huì)員是不是支付成功,改變定單狀態(tài)等。
String notify_url = TenPayConfig.notifyUrl;
String trade_type = "NATIVE";
// 商戶(hù)號(hào)
String mch_id = TenPayConfig.partner;
// 隨機(jī)字符串
String nonce_str = TenpayUtil.getNonceStr();
// 商品描寫(xiě)根據(jù)情況修改
String body = order.getBody();
// 商戶(hù)定單號(hào)
String out_trade_no = order.getOutTradeNo();
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", TenPayConfig.appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("attach", attach);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", totalFee);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
RequestHandler reqHandler = new RequestHandler(null, null);
reqHandler.init(TenPayConfig.appid, TenPayConfig.appsecret,
TenPayConfig.partnerkey);
String sign = reqHandler.createSign(packageParams);
String xml = "<xml>" + "<appid>" + TenPayConfig.appid + "</appid>"
+ "<mch_id>" + mch_id + "</mch_id>" + "<nonce_str>" + nonce_str
+ "</nonce_str>" + "<sign>" + sign + "</sign>"
+ "<body><![CDATA[" + body + "]]></body>" + "<out_trade_no>"
+ out_trade_no + "</out_trade_no>" + "<attach>" + attach
+ "</attach>" + "<total_fee>" + totalFee + "</total_fee>"
+ "<spbill_create_ip>" + spbill_create_ip
+ "</spbill_create_ip>" + "<notify_url>" + notify_url
+ "</notify_url>" + "<trade_type>" + trade_type
+ "</trade_type>" + "</xml>";
System.out.println(xml);
String code_url = "";
String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
code_url = new TenPayCore().getCodeUrl(createOrderURL, xml);//調(diào)用統(tǒng)1下單接口
if (code_url == null || code_url.equalsIgnoreCase("")) {
logger.debug("****************************trade has closed or no this trade in tencentPay");
response.sendError(404);
return;
}
GenerateQrCodeUtil.encodeQrcode(code_url, response);
}
2.公眾號(hào)支付
公眾號(hào)支付首先通過(guò)H5調(diào)起支付api,微信生成定單,然后開(kāi)發(fā)者獲得預(yù)支付id,最后由用戶(hù)確認(rèn)支付。
①H5調(diào)起JSAPI。
調(diào)起有兩種方式,1種是WeixinJSBridge.invoke(),另外一種是最新的微信JSAPI,兩種方式都可,官方文檔用的第1種,使用第1種方式首先要引入http://res.wx.qq.com/open/js/jweixin⑴.0.0.js。
function toPay(){
$.ajax({
url : URL,
type : "GET",
dataType : 'json',
success : function(data) {
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId" : data.appId, //公眾號(hào)名稱(chēng),由商戶(hù)傳入
"timeStamp" : data.timeStamp, //時(shí)間戳,自1970年以來(lái)的秒數(shù)
"nonceStr" : data.nonceStr, //隨機(jī)串
"package" : data.package,
"signType" : data.signType, //微信簽名方式:
"paySign" : data.paySign
//微信簽名
}, function(res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
} // 使用以上方式判斷前端返回:res.err_msg將在用戶(hù)支付成功后返回 ok,但其實(shí)不保證它絕對(duì)可靠。
else{
//res.err_msg;
}
});
}
});
}
②獲得預(yù)支付id
這個(gè)api和掃碼支付的api差不多,就是trade為JSAPI不1樣。
public void TencentPayController(@RequestParam("orderno")String orderno,HttpServletRequest request,
HttpServletResponse response) throws IOException {
//根據(jù)定單號(hào)查詢(xún)定單
OrderProduceBean order=reservationCarService.getTradebyNo(orderno);
String appid = TenPayConfig.appid;
String openId =order.getOpenId();
// 定單號(hào)
String orderId = order.getOutTradeNo();
// 附加數(shù)據(jù) 原樣返回
String attach = "attach";
// 總金額以分為單位,不帶小數(shù)點(diǎn)
String totalFee = TenpayUtil.getMoney(order.getTotalFee().toString());
// 定單生成的機(jī)器 IP
String spbill_create_ip = IpUtil.getIpAddr(request);;
// 這里notify_url是 支付完成后微信發(fā)給該鏈接信息,可以判斷會(huì)員是不是支付成功,改變定單狀態(tài)等。
String notify_url = TenPayConfig.notifyUrl;
String trade_type = "JSAPI";
// ---必須參數(shù)
// 商戶(hù)號(hào)
String mch_id = TenPayConfig.partner;
// 隨機(jī)字符串
String nonce_str = TenpayUtil.getNonceStr();
// 商品描寫(xiě)根據(jù)情況修改
String body = order.getBody();
// 商戶(hù)定單號(hào)
String out_trade_no = orderId;
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("attach", attach);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", totalFee);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("openid", openId);
RequestHandler reqHandler = new RequestHandler(null, null);
reqHandler.init(appid, TenPayConfig.appsecret,TenPayConfig.partnerkey);
String sign = reqHandler.createSign(packageParams);
String xml = "<xml>" + "<appid>" + appid + "</appid>" + "<mch_id>"
+ mch_id + "</mch_id>" + "<nonce_str>" + nonce_str
+ "</nonce_str>" + "<sign>" + sign + "</sign>"
+ "<body><![CDATA[" + body + "]]></body>"
+ "<out_trade_no>" + out_trade_no
+ "</out_trade_no>" + "<attach>" + attach + "</attach>"
+ "<total_fee>" + totalFee + "</total_fee>"
+ "<spbill_create_ip>" + spbill_create_ip
+ "</spbill_create_ip>" + "<notify_url>" + notify_url
+ "</notify_url>" + "<trade_type>" + trade_type
+ "</trade_type>" + "<openid>" + openId + "</openid>"
+ "</xml>";
String prepay_id = "";
String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
prepay_id = new TenPayCore().getPayNo(createOrderURL, xml);
logger.debug("********************************************");
logger.debug("prepay_id :" + prepay_id);
//獲得prepay_id后,拼接最后要求支付所需要的package
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
String timestamp = Sha1Util.getTimeStamp();
String packages = "prepay_id="+prepay_id;
finalpackage.put("appId", appid);
finalpackage.put("timeStamp", timestamp);
finalpackage.put("nonceStr", nonce_str);
finalpackage.put("package", packages);
finalpackage.put("signType", "MD5");
//要簽名
String finalsign = reqHandler.createSign(finalpackage);
finalpackage.put("paySign", finalsign);
String finaPackage = "\"appId\":\"" + appid + "\",\"timeStamp\":\"" + timestamp
+ "\",\"nonceStr\":\"" + nonce_str + "\",\"package\":\""
+ packages + "\",\"signType\" : \"MD5" + "\",\"paySign\":\""
+ finalsign + "\"";
logger.debug("********************************************");
logger.debug("V3 jsApi package:" + finaPackage);
if(prepay_id.length()>0){
this.callBack(response, request,this.gson.toJson(finalpackage));
return;
}else{
this.callBack(response, request,"{\"error\":\"prepay_id is null\"}");
return;
}
}
3.支付回調(diào)
該回調(diào)方法的路徑就是獲得預(yù)支付id中的參數(shù)notify_url,用來(lái)處理支付完成后的業(yè)務(wù)邏輯,注意1定要檢查return_code和result_code是不是都為success,另外1定要給微信返回success信息.
public void notifyPay(HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 示例報(bào)文
String inputLine;
String notityXml = "";
String resXml = "";
try {
while ((inputLine = request.getReader().readLine()) != null) {
notityXml += inputLine;
}
request.getReader().close();
} catch (Exception e) {
e.printStackTrace();
}
Map<String, String> m = TenpayUtil.parseXmlToList(notityXml);
OrderTenPayBean orderTen = new OrderTenPayBean();
orderTen.setAppid(m.get("appid").toString());
orderTen.setBankType(m.get("bank_type").toString());
orderTen.setCashFee(m.get("cash_fee").toString());
orderTen.setFeeType(m.get("fee_type").toString());
orderTen.setIsSubscribe(m.get("is_subscribe").toString());
orderTen.setMchId(m.get("mch_id").toString());
orderTen.setNonceStr(m.get("nonce_str").toString());
orderTen.setOpenid(m.get("openid").toString());
orderTen.setOutTradeNo(m.get("out_trade_no").toString());
orderTen.setResultCode(m.get("result_code").toString());
orderTen.setReturnCode(m.get("return_code").toString());
orderTen.setSign(m.get("sign").toString());
orderTen.setTimeEnd(m.get("time_end").toString());
orderTen.setTotalFee(m.get("total_fee").toString());
orderTen.setTradeType(m.get("trade_type").toString());
orderTen.setTransactionId(m.get("transaction_id").toString());
if ("SUCCESS".equals(orderTen.getResultCode()) && "SUCCESS".equals(orderTen.getReturnCode())<span style="font-family: Arial, Helvetica, sans-serif;">) { //必須判斷</span>
//TODO 信息入庫(kù),修改定單狀態(tài)
// 支付成功,必須給微信返回此信息,否則微信會(huì)1直調(diào)用此回調(diào)方法
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[報(bào)文為空]]></return_msg>" + "</xml> ";
}
this.callBack(response, request, resXml);
}
4.退款
退款需要把商戶(hù)證書(shū)apiclient_cert.p12放到指定目錄下,由于有證書(shū)的情況下退款不需要密碼,1定要注意業(yè)務(wù)要求的權(quán)限問(wèn)題
public void refusePay(HttpServletResponse response,HttpServletRequest request,@PathVariable(value="outTradeNo")String outTradeNo) throws Exception {
OrderProduceBean tradebyNo = reservationCarService.getTradebyNo(outTradeNo);
SortedMap<String, String> parameters = new TreeMap<String, String>();
parameters.put("appid", TenPayConfig.appid);
parameters.put("mch_id", TenPayConfig.partner);
parameters.put("nonce_str", TenpayUtil.getNonceStr());
parameters.put("out_trade_no", tradebyNo.getOutTradeNo());
parameters.put("out_refund_no", tradebyNo.getOutTradeNo()); // 我們自己設(shè)定的退款申請(qǐng)?zhí)枺`為UK
parameters.put("total_fee", TenpayUtil.getMoney(tradebyNo.getTotalFee().toString())); // 單位為分
parameters.put("refund_fee", TenpayUtil.getMoney(tradebyNo.getTotalFee().toString())); // 單位為分
parameters.put("op_user_id", TenPayConfig.partner);
RequestHandler reqHandler = new RequestHandler(null, null);
reqHandler.init(TenPayConfig.appid, TenPayConfig.appsecret,
TenPayConfig.partnerkey);
String sign = reqHandler.createSign(parameters);
parameters.put("sign", sign);
String reuqestXml = TenpayUtil.getRequestXml(parameters);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File(Resources.getString("cert_url")));
try {
keyStore.load(instream, TenPayConfig.partner.toCharArray());
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, TenPayConfig.partner.toCharArray())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
System.out.println("executing request" + httpPost.getRequestLine());
StringEntity reqEntity = new StringEntity(reuqestXml);
// 設(shè)置類(lèi)型
reqEntity.setContentType("application/x-www-form-urlencoded");
httpPost.setEntity(reqEntity);
CloseableHttpResponse reqs = httpclient.execute(httpPost);
try {
HttpEntity entity = reqs.getEntity();
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String returnXml = "";
String text;
while ((text = bufferedReader.readLine()) != null) {
returnXml += text;
}
//微信退款返回的參數(shù)轉(zhuǎn)換為Map
Map<String, String> resultMaP = TenpayUtil.parseXmlToList(returnXml);
this.callBack(response, request, resultMaP.toString());
//TODO 信息入庫(kù)
}
EntityUtils.consume(entity);
} finally {
reqs.close();
}
} finally {
httpclient.close();
}
}
PS:代碼僅供參考!
生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)