微信App支付服務(wù)端的實(shí)現(xiàn)方法,供大家參考,具體內(nèi)容如下
創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、代縣網(wǎng)絡(luò)推廣、小程序定制開發(fā)、代縣網(wǎng)絡(luò)營銷、代縣企業(yè)策劃、代縣品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供代縣建站搭建服務(wù),24小時(shí)服務(wù)熱線:18982081108,官方網(wǎng)址:www.chinadenli.net
引言
主要實(shí)現(xiàn)app支付統(tǒng)一下單、異步通知、調(diào)起支付接口、支付訂單查詢、申請退款、查詢退款功能;封裝了https對發(fā)起退款的證書校驗(yàn)、簽名、xml解析等。
支付流程
具體支付流程參考“微信APP”文檔,文檔地址
APP支付:APP端點(diǎn)擊下單—-服務(wù)端生成訂單,并調(diào)起“統(tǒng)一下單”,返回app支付所需參數(shù)—–APP端“調(diào)起支付接口“,發(fā)起支付—-微信服務(wù)器端調(diào)用服務(wù)端回調(diào)地址—–服務(wù)端按照“支付結(jié)果通知”,處理支付結(jié)果
app查詢:調(diào)起“查詢訂單”
APP退款:發(fā)起退款請求,調(diào)用“申請退款”,發(fā)起退款,需雙向證書驗(yàn)證
APP退款查詢:調(diào)起“查詢退款”

支付代碼實(shí)現(xiàn)
代碼實(shí)現(xiàn)簽名、證書校驗(yàn)、http和https封裝等,項(xiàng)目結(jié)構(gòu)如下:

支付代碼
包含支付、支付查詢、異步通知、退款申請、退款查詢
package org.andy.wxpay.controller;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.andy.wxpay.model.JsonResult;
import org.andy.wxpay.model.ResponseData;
import org.andy.wxpay.utils.CollectionUtil;
import org.andy.wxpay.utils.ConfigUtil;
import org.andy.wxpay.utils.FileUtil;
import org.andy.wxpay.utils.HttpUtils;
import org.andy.wxpay.utils.PayUtil;
import org.andy.wxpay.utils.SerializerFeatureUtil;
import org.andy.wxpay.utils.StringUtil;
import org.andy.wxpay.utils.WebUtil;
import org.andy.wxpay.utils.XmlUtil;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.alibaba.fastjson.JSON;
/**
* 創(chuàng)建時(shí)間:2016年11月2日 下午4:16:32
*
* @author andy
* @version 2.2
*/
@Controller
@RequestMapping("/order")
public class PayController {
private static final Logger LOG = Logger.getLogger(PayController.class);
private static final String ORDER_PAY = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // 統(tǒng)一下單
private static final String ORDER_PAY_QUERY = "https://api.mch.weixin.qq.com/pay/orderquery"; // 支付訂單查詢
private static final String ORDER_REFUND = "https://api.mch.weixin.qq.com/secapi/pay/refund"; // 申請退款
private static final String ORDER_REFUND_QUERY = "https://api.mch.weixin.qq.com/pay/refundquery"; // 申請退款
private static final String APP_ID = ConfigUtil.getProperty("wx.appid");
private static final String MCH_ID = ConfigUtil.getProperty("wx.mchid");
private static final String API_SECRET = ConfigUtil.getProperty("wx.api.secret");
/**
* 支付下訂單
*
* @param request
* @param response
* @param cashnum
* 支付金額
* @param mercid
* 商品id
* @param callback
*/
@RequestMapping(value = "/pay", method = RequestMethod.POST)
public void orderPay(HttpServletRequest request, HttpServletResponse response,
@RequestParam(required = false, defaultValue = "0") Double cashnum, String mercid, String callback) {
LOG.info("[/order/pay]");
if (!"001".equals(mercid)) {
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "商品不存在", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
Map<String, String> restmap = null;
boolean flag = true; // 是否訂單創(chuàng)建成功
try {
String total_fee = BigDecimal.valueOf(cashnum).multiply(BigDecimal.valueOf(100))
.setScale(0, BigDecimal.ROUND_HALF_UP).toString();
Map<String, String> parm = new HashMap<String, String>();
parm.put("appid", APP_ID);
parm.put("mch_id", MCH_ID);
parm.put("device_info", "WEB");
parm.put("nonce_str", PayUtil.getNonceStr());
parm.put("body", "測試付費(fèi)");
parm.put("attach", "Andy");
parm.put("out_trade_no", PayUtil.getTradeNo());
parm.put("total_fee", total_fee);
parm.put("spbill_create_ip", PayUtil.getRemoteAddrIp(request));
parm.put("notify_url", "https://www.andy.org/wxpay/order/pay/notify.shtml");
parm.put("trade_type", "APP");
parm.put("sign", PayUtil.getSign(parm, API_SECRET));
String restxml = HttpUtils.post(ORDER_PAY, XmlUtil.xmlFormat(parm, false));
restmap = XmlUtil.xmlParse(restxml);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
Map<String, String> payMap = new HashMap<String, String>();
if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {
payMap.put("appid", APP_ID);
payMap.put("partnerid", MCH_ID);
payMap.put("prepayid", restmap.get("prepay_id"));
payMap.put("package", "Sign=WXPay");
payMap.put("noncestr", PayUtil.getNonceStr());
payMap.put("timestamp", PayUtil.payTimestamp());
try {
payMap.put("sign", PayUtil.getSign(payMap, API_SECRET));
} catch (Exception e) {
flag = false;
}
}
if (flag) {
WebUtil.response(response,
WebUtil.packJsonp(callback,
JSON.toJSONString(new JsonResult(1, "訂單獲取成功", new ResponseData(null, payMap)),
SerializerFeatureUtil.FEATURES)));
} else {
if (CollectionUtil.isNotEmpty(restmap)) {
LOG.info("訂單創(chuàng)建失敗:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
}
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "訂單獲取失敗", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
}
/**
* 查詢支付結(jié)果
*
* @param request
* @param response
* @param tradeid 微信交易訂單號
* @param tradeno 商品訂單號
* @param callback
*/
@RequestMapping(value = "/pay/query", method = RequestMethod.POST)
public void orderPayQuery(HttpServletRequest request, HttpServletResponse response, String tradeid, String tradeno,
String callback) {
LOG.info("[/order/pay/query]");
if (StringUtil.isEmpty(tradeno) && StringUtil.isEmpty(tradeid)) {
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "訂單號不能為空", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
Map<String, String> restmap = null;
try {
Map<String, String> parm = new HashMap<String, String>();
parm.put("appid", APP_ID);
parm.put("mch_id", MCH_ID);
parm.put("transaction_id", tradeid);
parm.put("out_trade_no", tradeno);
parm.put("nonce_str", PayUtil.getNonceStr());
parm.put("sign", PayUtil.getSign(parm, API_SECRET));
String restxml = HttpUtils.post(ORDER_PAY_QUERY, XmlUtil.xmlFormat(parm, false));
restmap = XmlUtil.xmlParse(restxml);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {
// 訂單查詢成功 處理業(yè)務(wù)邏輯
LOG.info("訂單查詢:訂單" + restmap.get("out_trade_no") + "支付成功");
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(1, "訂單支付成功", new ResponseData()), SerializerFeatureUtil.FEATURES)));
} else {
if (CollectionUtil.isNotEmpty(restmap)) {
LOG.info("訂單支付失敗:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
}
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "訂單支付失敗", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
}
/**
* 訂單支付微信服務(wù)器異步通知
*
* @param request
* @param response
*/
@RequestMapping("/pay/notify")
public void orderPayNotify(HttpServletRequest request, HttpServletResponse response) {
LOG.info("[/order/pay/notify]");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/xml");
try {
ServletInputStream in = request.getInputStream();
String resxml = FileUtil.readInputStream2String(in);
Map<String, String> restmap = XmlUtil.xmlParse(resxml);
LOG.info("支付結(jié)果通知:" + restmap);
if ("SUCCESS".equals(restmap.get("result_code"))) {
// 訂單支付成功 業(yè)務(wù)處理
String out_trade_no = restmap.get("out_trade_no"); // 商戶訂單號
// 通過商戶訂單判斷是否該訂單已經(jīng)處理 如果處理跳過 如果未處理先校驗(yàn)sign簽名 再進(jìn)行訂單業(yè)務(wù)相關(guān)的處理
String sing = restmap.get("sign"); // 返回的簽名
restmap.remove("sign");
String signnow = PayUtil.getSign(restmap, API_SECRET);
if (signnow.equals(sing)) {
// 進(jìn)行業(yè)務(wù)處理
LOG.info("訂單支付通知: 支付成功,訂單號" + out_trade_no);
// 處理成功后相應(yīng)給響應(yīng)xml
Map<String, String> respMap = new HashMap<>();
respMap = new HashMap<String, String>();
respMap.put("return_code", "SUCCESS"); //相應(yīng)給微信服務(wù)器
respMap.put("return_msg", "OK");
String resXml = XmlUtil.xmlFormat(restmap, true);
response.getWriter().write(resXml);
} else {
LOG.info("訂單支付通知:簽名錯(cuò)誤");
}
} else {
LOG.info("訂單支付通知:支付失敗," + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
/**
* 訂單退款 需要雙向證書驗(yàn)證
*
* @param request
* @param response
* @param tradeno 微信訂單號
* @param orderno 商家訂單號
* @param callback
*/
@RequestMapping(value = "/pay/refund", method = RequestMethod.POST)
public void orderPayRefund(HttpServletRequest request, HttpServletResponse response, String tradeno, String orderno,
String callback) {
LOG.info("[/pay/refund]");
if (StringUtil.isEmpty(tradeno) && StringUtil.isEmpty(orderno)) {
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "訂單號不能為空", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
Map<String, String> restmap = null;
try {
Map<String, String> parm = new HashMap<String, String>();
parm.put("appid", APP_ID);
parm.put("mch_id", MCH_ID);
parm.put("nonce_str", PayUtil.getNonceStr());
parm.put("transaction_id", tradeno);
parm.put("out_trade_no", orderno);//訂單號
parm.put("out_refund_no", PayUtil.getRefundNo()); //退款單號
parm.put("total_fee", "10"); // 訂單總金額 從業(yè)務(wù)邏輯獲取
parm.put("refund_fee", "10"); // 退款金額
parm.put("op_user_id", MCH_ID);
parm.put("refund_account", "REFUND_SOURCE_RECHARGE_FUNDS");//退款方式
parm.put("sign", PayUtil.getSign(parm, API_SECRET));
//String restxml = HttpUtils.posts(ORDER_REFUND, XmlUtil.xmlFormat(parm, false));
String restxml = HttpUtils.posts(ORDER_REFUND, XmlUtil.xmlFormat(parm, false));
restmap = XmlUtil.xmlParse(restxml);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
Map<String, String> refundMap = new HashMap<>();
if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {
refundMap.put("transaction_id", restmap.get("transaction_id"));
refundMap.put("out_trade_no", restmap.get("out_trade_no"));
refundMap.put("refund_id", restmap.get("refund_id"));
refundMap.put("out_refund_no", restmap.get("out_refund_no"));
LOG.info("訂單退款:訂單" + restmap.get("out_trade_no") + "退款成功,商戶退款單號" + restmap.get("out_refund_no") + ",微信退款單號"
+ restmap.get("refund_id"));
WebUtil.response(response,
WebUtil.packJsonp(callback,
JSON.toJSONString(new JsonResult(1, "訂單獲取成功", new ResponseData(null, refundMap)),
SerializerFeatureUtil.FEATURES)));
} else {
if (CollectionUtil.isNotEmpty(restmap)) {
LOG.info("訂單退款失敗:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
}
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "訂單退款失敗", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
}
/**
* 訂單退款查詢
* @param request
* @param response
* @param tradeid 微信訂單號
* @param tradeno 商戶訂單號
* @param refundid 微信退款號
* @param refundno 商家退款號
* @param callback
*/
@RequestMapping(value = "/pay/refund/query", method = RequestMethod.POST)
public void orderPayRefundQuery(HttpServletRequest request, HttpServletResponse response, String refundid,
String refundno, String tradeid, String tradeno, String callback) {
LOG.info("[/pay/refund/query]");
if (StringUtil.isEmpty(tradeid) && StringUtil.isEmpty(tradeno)
&& StringUtil.isEmpty(refundno) && StringUtil.isEmpty(refundid)) {
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "退單號或訂單號不能為空", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
Map<String, String> restmap = null;
try {
Map<String, String> parm = new HashMap<String, String>();
parm.put("appid", APP_ID);
parm.put("mch_id", MCH_ID);
parm.put("transaction_id", tradeid);
parm.put("out_trade_no", tradeno);
parm.put("refund_id", refundid);
parm.put("out_refund_no", refundno);
parm.put("nonce_str", PayUtil.getNonceStr());
parm.put("sign", PayUtil.getSign(parm, API_SECRET));
String restxml = HttpUtils.post(ORDER_REFUND_QUERY, XmlUtil.xmlFormat(parm, false));
restmap = XmlUtil.xmlParse(restxml);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
Map<String, String> refundMap = new HashMap<>();
if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code")) && "SUCCESS".equals(restmap.get("result_code"))) {
// 訂單退款查詢成功 處理業(yè)務(wù)邏輯
LOG.info("退款訂單查詢:訂單" + restmap.get("out_trade_no") + "退款成功,退款狀態(tài)"+ restmap.get("refund_status_0"));
refundMap.put("transaction_id", restmap.get("transaction_id"));
refundMap.put("out_trade_no", restmap.get("out_trade_no"));
refundMap.put("refund_id", restmap.get("refund_id_0"));
refundMap.put("refund_no", restmap.get("out_refund_no_0"));
refundMap.put("refund_status", restmap.get("refund_status_0"));
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(1, "訂單退款成功", new ResponseData(null, refundMap)), SerializerFeatureUtil.FEATURES)));
} else {
if (CollectionUtil.isNotEmpty(restmap)) {
LOG.info("訂單退款失敗:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
}
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "訂單退款失敗", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
}
}
微信支付接口參數(shù)含義具體參考微信APP支付文檔。
微信支付工具類
包含簽名、訂單號、退單號、隨機(jī)串、服務(wù)器ip地址、客戶端ip地址等方法。
package org.andy.wxpay.utils;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
/**
* 創(chuàng)建時(shí)間:2016年11月2日 下午7:12:44
*
* @author andy
* @version 2.2
*/
public class PayUtil {
/**
* 生成訂單號
*
* @return
*/
public static String getTradeNo() {
// 自增8位數(shù) 00000001
return "TNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
}
/**
* 退款單號
*
* @return
*/
public static String getRefundNo() {
// 自增8位數(shù) 00000001
return "RNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
}
/**
* 退款單號
*
* @return
*/
public static String getTransferNo() {
// 自增8位數(shù) 00000001
return "TNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
}
/**
* 返回客戶端ip
*
* @param request
* @return
*/
public static String getRemoteAddrIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
// 多次反向代理后會有多個(gè)ip值,第一個(gè)ip才是真實(shí)ip
int index = ip.indexOf(",");
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}
/**
* 獲取服務(wù)器的ip地址
*
* @param request
* @return
*/
public static String getLocalIp(HttpServletRequest request) {
return request.getLocalAddr();
}
public static String getSign(Map<String, String> params, String paternerKey) throws UnsupportedEncodingException {
return MD5Utils.getMD5(createSign(params, false) + "&key=" + paternerKey).toUpperCase();
}
/**
* 構(gòu)造簽名
*
* @param params
* @param encode
* @return
* @throws UnsupportedEncodingException
*/
public static String createSign(Map<String, String> params, boolean encode) throws UnsupportedEncodingException {
Set<String> keysSet = params.keySet();
Object[] keys = keysSet.toArray();
Arrays.sort(keys);
StringBuffer temp = new StringBuffer();
boolean first = true;
for (Object key : keys) {
if (key == null || StringUtil.isEmpty(params.get(key))) // 參數(shù)為空不參與簽名
continue;
if (first) {
first = false;
} else {
temp.append("&");
}
temp.append(key).append("=");
Object value = params.get(key);
String valueStr = "";
if (null != value) {
valueStr = value.toString();
}
if (encode) {
temp.append(URLEncoder.encode(valueStr, "UTF-8"));
} else {
temp.append(valueStr);
}
}
return temp.toString();
}
/**
* 創(chuàng)建支付隨機(jī)字符串
* @return
*/
public static String getNonceStr(){
return RandomUtil.randomString(RandomUtil.LETTER_NUMBER_CHAR, 32);
}
/**
* 支付時(shí)間戳
* @return
*/
public static String payTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
其他所需工具類參考項(xiàng)目源碼
支付結(jié)果

APP支付測試完成
源代碼地址:微信APP支付
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
網(wǎng)站標(biāo)題:java實(shí)現(xiàn)微信App支付服務(wù)端
當(dāng)前鏈接:http://www.chinadenli.net/article18/pgeodp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、Google、關(guān)鍵詞優(yōu)化、用戶體驗(yàn)、小程序開發(fā)、軟件開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)