微信小程序支付以及退款功能(超详细)
日期: 2020-04-10 分类: 跨站数据 317次阅读
Springboot整合微信小程序支付
第一步:
先准备好
appID:小程序id
mchID: 商户号
key: 秘钥
certPath:从微信商户平台下载的安全证书存放的路径(apiclient_cert.p12的路径)
payNotifyUrl: 微信支付成功的异步通知接口 (小程序的话这个路径必须是https的)
第二步:导入微信支付的依赖
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
第三步:在springboot核心配置文件中配置参数
#微信app支付
pay:
wxpay:
app:
appID:小程序id
mchID: 商户号
key: ak秘钥
certPath:从微信商户平台下载的安全证书存放的路径(apiclient_cert.p12的路径)
payNotifyUrl: 微信支付成功的异步通知接口 (小程序的话这个路径必须是https的)
第四步:创建配置类WxPayAppConfig
下面展示一些 内联代码片
。
/**
* 配置我们自己的信息
*/
@Component
@ConfigurationProperties(prefix = "pay.wxpay.app")
public class WxPayAppConfig implements WXPayConfig {
/**
* appID
*/
private String appID;
/**
* 商户号
*/
private String mchID;
/**
* API 密钥
*/
private String key;
/**
* API证书绝对路径 (本项目放在了 resources/cert/wxpay/apiclient_cert.p12")
*/
private String certPath;
/**
* HTTP(S) 连接超时时间,单位毫秒
*/
private int httpConnectTimeoutMs = 8000;
/**
* HTTP(S) 读数据超时时间,单位毫秒
*/
private int httpReadTimeoutMs = 10000;
/**
* 微信支付异步通知地址
*/
private String payNotifyUrl;
/**
* 微信退款异步通知地址
*/
private String refundNotifyUrl;
/**
* 获取商户证书内容(这里证书需要到微信商户平台进行下载)
*
* @return 商户证书内容
*/
@Override
public InputStream getCertStream() {
InputStream certStream =getClass().getClassLoader().getResourceAsStream(certPath);
return certStream;
}
第五步:支付方法
service层方法
下面展示 支付代码片
。
public ResultMap unifiedOrder(String orderNo, double amount, String body,String spbill_create_ip) {
Map<String, String> returnMap = new HashMap<>();
Map<String, String> responseMap = new HashMap<>();
Map<String, String> requestMap = new HashMap<>();
try {
Long time = System.currentTimeMillis() / 1000;
String timestamp = time.toString();
WXPay wxpay = new WXPay(wxPayAppConfig);
requestMap.put("body", body); // 商品描述
requestMap.put("out_trade_no", orderNo); // 商户订单号
requestMap.put("total_fee", String.valueOf((int)(amount*100))); // 总金额
requestMap.put("spbill_create_ip",spbill_create_ip); // 终端IP
requestMap.put("trade_type", "APP"); // App支付类型
Orders orders = ordersMapper.FindOrder(orderNo);
UserInfo u =userMapper.seluserByid(orders.getOpenId());
String payVerifApp = u.getPayVerifApp();
requestMap.put("openid",payVerifApp);
requestMap.put("notify_url", wxPayAppConfig.getPayNotifyUrl()); // 接收微信支付异步通知回调地址
Map<String, String> resultMap = wxpay.unifiedOrder(requestMap);
//获取返回码
String returnCode = resultMap.get("return_code");
String returnMsg = resultMap.get("return_msg");
//若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
if ("SUCCESS".equals(returnCode)) {
/* int update =ordersMapper.updateOrder(DateUtil.now(),orderNo);*/
String resultCode = resultMap.get("result_code");
String errCodeDes = resultMap.get("err_code_des");
if ("SUCCESS".equals(resultCode)) {
responseMap = resultMap;
}
}
if (responseMap == null || responseMap.isEmpty()) {
return ResultMap.error("获取预支付交易会话标识失败");
}
// 3、签名生成算法
returnMap.put("appid",wxPayAppConfig.getAppID());
returnMap.put("partnerid",wxPayAppConfig.getMchID());
returnMap.put("prepayid", responseMap.get("prepay_id"));
returnMap.put("noncestr", responseMap.get("nonce_str"));
returnMap.put("timestamp", timestamp);
returnMap.put("package", "Sign=WXPay");
returnMap.put("sign", WXPayUtil.generateSignature(returnMap,wxPayAppConfig.getKey()));//微信支付签名
return ResultMap.ok().put("data", returnMap);
} catch (Exception e) {
logger.error("订单号:{},错误信息:{}", orderNo, e.getMessage());
return ResultMap.error("微信支付统一下单失败");
}
}
下面展示 支付回调方法
。
// An highlighted block
public String notify(String notifyStr) {
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
try {
// 转换成map
Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
WXPay wxpayApp = new WXPay(wxPayAppConfig);
if (wxpayApp.isPayResultNotifySignatureValid(resultMap)) {
String returnCode = resultMap.get("return_code"); //状态
String outTradeNo = resultMap.get("out_trade_no");//商户订单号
String transactionId = resultMap.get("transaction_id");
if (returnCode.equals("SUCCESS")) {
if (StringUtils.isNotBlank(outTradeNo)) {
/**
* 注意!!!
* 请根据业务流程,修改数据库订单支付状态,和其他数据的相应状态
*/
xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}
}else {
}
}
} catch (Exception e) {
e.printStackTrace();
}
return xmlBack;
}
下面展示退款代码片
。
@Override
public ResultMap refund(String orderNo){
//获取订单
Orders orders = ordersMapper.FindOrder(orderNo);
String checkInTime = orders.getCheckInTime();
Integer payState = orders.getPayState();
Double amount=orders.getFactPrice();
//判断订单是否可以退款
if (payState != 1 && payState != 8 && payState != 5 && payState != 10) {
return ResultMap.error("该订单无法申请退款");
}
SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
String today = formatter.format(date);
int compareTo = today.compareTo(checkInTime);
if(payState != 8){
if (compareTo >= 0 || BetweenDate.dayBetween(today, checkInTime).size() == 1) {
return ResultMap.error("该订单无法申请退款");
}
}
if(StringUtils.isBlank(orderNo)){
return ResultMap.error("订单编号不能为空");
}
if(amount <= 0){
return ResultMap.error("退款金额必须大于0");
}
//退款开始
Map<String, String> responseMap = new HashMap<>();
/* Map<String, String> requestMap = new HashMap<>();*/
/* WXPay wxpay = new WXPay(wxPayAppConfig);*/
Map<String, String> resultData= new HashMap<>();
try {
/* requestMap.put("appid",wxPayAppConfig.getAppID());
requestMap.put("op_user_id",wxPayAppConfig.getMchID());
requestMap.put("nonce_str", orderNo);
requestMap.put("out_trade_no", orderNo);
requestMap.put("out_refund_no",orderNo);
*//* requestMap.put("out_refund_no", UUIDGenerator.getOrderNo());*//*
requestMap.put("total_fee", String.valueOf(amount));
requestMap.put("refund_fee", String.valueOf((int)(amount*100)));//所需退款金额
requestMap.put("refund_desc", "民宿退款");
requestMap.put("refund_fee_type", "CNY");
requestMap.put("sign", WXPayUtil.generateSignature(requestMap,wxPayAppConfig.getKey()));
responseMap = wxpay.refund(requestMap);*/
String outRefundNo = IdUtil.fastSimpleUUID();
DecimalFormat df = new DecimalFormat("######0");
String total_fee = String.valueOf(df.format((orders.getFactPrice() * 100)));
HashMap<String, String> map = new HashMap<String, String>();
map.put("appid", wxPayAppConfig.getAppID());
map.put("mch_id", wxPayAppConfig.getMchID());
map.put("nonce_str", outRefundNo);
map.put("out_trade_no", orderNo);
map.put("out_refund_no", outRefundNo);
map.put("total_fee", total_fee);
map.put("refund_fee", total_fee);
map.put("op_user_id", wxPayAppConfig.getMchID());
map.put("refund_fee_type", "CNY");
String paySign = getPayCustomSign(map, wxPayAppConfig.getKey());
map.put("sign", paySign);
String xml = ArrayToXml(map);
String url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
String post = post(url, xml);
resultData = doXMLParse(post);
} catch (Exception e) {
e.printStackTrace();
}
String return_code = resultData.get("return_code"); //返回状态码
String return_msg = resultData.get("return_msg"); //返回信息
if ("SUCCESS".equals(return_code)) {
String result_code = resultData.get("result_code"); //业务结果
String err_code_des = resultData.get("err_code_des"); //错误代码描述
if ("SUCCESS".equals(result_code)) {
//修改用户订单状态为已退款
Integer refund = ordersMapper.refund(orderNo);
return ResultMap.ok("退款申请成功");
} else {
logger.info("订单号:{}错误信息:{}", orderNo, err_code_des);
return ResultMap.error(err_code_des);
}
} else {
logger.info("订单号:{}错误信息:{}", orderNo, return_msg);
return ResultMap.error(return_msg);
}
}
支付所需要的方法 代码片
。
public static String getPayCustomSign(HashMap<String, String> bizObj, String key) throws Exception {
String bizString = FormatBizQueryParaMap(bizObj, false);
return sign(bizString, key);
}
public static String FormatBizQueryParaMap(HashMap<String, String> paraMap, boolean urlencode) throws Exception {
String buff = "";
try {
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(paraMap.entrySet());
Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(o2.getKey());
}
});
for (int i = 0; i < infoIds.size(); i++) {
Map.Entry<String, String> item = infoIds.get(i);
if (item.getKey() != "") {
String key = item.getKey();
String val = item.getValue();
if (urlencode) {
val = URLEncoder.encode(val, "utf-8");
}
buff += key + "=" + val + "&";
}
}
if (buff.isEmpty() == false) {
buff = buff.substring(0, buff.length() - 1);
}
} catch (Exception e) {
throw new Exception(e.getMessage());
}
return buff;
}
public static String sign(String content, String key) throws Exception {
String signStr = "";
signStr = content + "&key=" + key;
return MD5Util.md5Encrypt32Upper(signStr);
}
public static String ArrayToXml(Map<String, String> arr) {
String xml = "<xml>";
Iterator<Map.Entry<String, String>> iter = arr.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, String> entry = iter.next();
String key = entry.getKey();
String val = entry.getValue();
if (IsNumeric(val)) {
xml += "<" + key + ">" + val + "</" + key + ">";
} else
xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">";
}
xml += "</xml>";
return xml;
}
public static boolean IsNumeric(String str) {
if (str.matches("\\d *")) {
return true;
} else {
return false;
}
}
private static String post(String url, String xmlParam) {
StringBuilder sb = new StringBuilder();
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File("apiclient_cert.p12文件地址"));
try {
keyStore.load(instream, "商户号".toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "商户号".toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" },
null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpPost httpPost = new HttpPost(url);
StringEntity reqEntity = new StringEntity(xmlParam);
reqEntity.setContentType("application/x-www-form-urlencoded");
httpPost.setEntity(reqEntity);
CloseableHttpResponse response = client.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
System.out.println(response.getStatusLine());
if (entity != null) {
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(entity.getContent(), "UTF-8"));
String text = "";
while ((text = bufferedReader.readLine()) != null) {
sb.append(text);
}
}
EntityUtils.consume(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
private static Map<String, String> doXMLParse(String xml) throws XmlPullParserException, IOException {
InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
Map<String, String> map = null;
XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser();
pullParser.setInput(inputStream, "UTF-8");
int eventType = pullParser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
map = new HashMap<String, String>();
break;
case XmlPullParser.START_TAG:
String key = pullParser.getName();
if (key.equals("xml"))
break;
String value = pullParser.nextText();
map.put(key, value);
break;
case XmlPullParser.END_TAG:
break;
}
eventType = pullParser.next();
}
return map;
}
// 获取IP
public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = ip.indexOf(",");
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}
Controller层方法
支付接口
public ResultMap unifiedOrder(@RequestParam("orderNo") String orderNo,@RequestParam("payType") Integer payType,
HttpServletRequest request){
try {
// 1、验证订单是否存在
// 2、开始微信支付统一下单
Orders orders = ordersService.FindOrder(orderNo);
ResultMap resultMap =null;
if(orders!=null){
String spbill_create_ip = getIpAddr(request);
resultMap = wxPayService.unifiedOrder(订单号,总价格,"商家名称",spbill_create_ip);
return resultMap;
}
return resultMap;//系统通用的返回结果集
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
return ResultMap.error("服务器异常,付款失败");
}
}
获取终端ip的方法
public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = ip.indexOf(",");
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}
微信支付异步通知接口
/**
* 微信支付异步通知
*/
public String payNotify(HttpServletRequest request) {
InputStream is = null;
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
try {
is = request.getInputStream();
// 将InputStream转换成String
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
xmlBack = wxPayService.notify(sb.toString());
} catch (Exception e) {
System.out.println("微信手机支付回调通知失败:"+e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return xmlBack;
}
微信退款接口
/**
*退款
* @param orderNo 订单号
* @return
*/
public ResultMap enterrefund(String orderNo){
ResultMap refund = wxPayService.refund(orderNo);
return refund;
}
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:小程序 java app
精华推荐