准备工作:已通过微信认证的公众号, 必须通过ICP备案域名(否则会报支付失败)
借鉴了很多大神的文章,在此先谢过了
大体过程:先扫码(还没有确定实际要支付的金额),这个码是商品的二维码,再生成订单,适用于自动贩卖机之类固定金额的。
模式一支付的流程如下图,稍微有点复杂
业务流程说明:
(1)商户后台系统根据微信支付规定格式生成二维码(规则见下文),展示给用户扫码。
(2)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(3)微信支付系统收到客户端请求,发起对商户后台系统支付回调URL的调用。调用请求将带productid和用户的openid等参数,并要求商户系统返回交数据包
(4)商户后台系统收到微信支付系统的回调请求,根据productid生成商户系统的订单。
(5)商户系统调用微信支付【统一下单API】请求下单,获取交易会话标识(prepay_id)。
(6)微信支付系统根据商户系统的请求生成预支付交易,并返回交易会话标识(prepay_id)。
(7)商户后台系统得到交易会话标识prepay_id(2小时内有效)。
(8)商户后台系统将prepay_id返回给微信支付系统。
(9)微信支付系统根据交易会话标识,发起用户端授权支付流程。
(10)用户在微信客户端输入密码,确认支付后,微信客户端提交支付授权。
(11)微信支付系统验证后扣款,完成支付交易。
(12)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(13)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(14)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(15)商户确认订单已支付后给用户发货。
一、设置回调地址
商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改,如下图所示。
这个支付回调的URL设置的作用是接收用户扫码后微信支付系统发送的数据,根据接收的数据生成商户系统的支付订单返回给微信支付系统,调用【统一下单API】提交支付交易。
二、生成微信支付二维码
参考文档https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_6
二维码长链接示例:
weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
这样太冗长了,转换成短链接 weixin://wxpay/bizpayurl?pr=XXXX
/**
* 扫码支付模式一生成二维码
*
* @param productId 商品ID
* @throws IOException
*/
@GetMapping("payone")
public Map<String, Object> payone(String productId) {
Map<String, Object> data = new HashMap<>();
String nonce_str = PayUtil.createNonceStr();
// String product_id = "product_001"; // 推荐根据商品ID生成
TreeMap<String, Object> packageParams = new TreeMap<>();
packageParams.put("appid", WechatConfig.APP_ID);
packageParams.put("mch_id", WechatConfig.MCH_ID);
packageParams.put("product_id", productId);
packageParams.put("time_stamp", PayUtil.createTimeStamp());
packageParams.put("nonce_str", nonce_str);
String str_url = PayUtil.createPayImageUrl(packageParams);
String sign = SignatureUtil.createSign(packageParams, WechatConfig.API_KEY,
SystemConfig.DEFAULT_CHARACTER_ENCODING);
packageParams.put("sign", sign);
String payurl = "weixin://wxpay/bizpayurl?sign=" + sign + str_url;
logger.debug("payurl is {}", payurl);
/**** 转成短链接 ****/
PayShortUrlParams payShortUrlParams = new PayShortUrlParams();
payShortUrlParams.setAppid(WechatConfig.APP_ID);
payShortUrlParams.setMch_id(WechatConfig.MCH_ID);
payShortUrlParams.setLong_url(payurl);
payShortUrlParams.setNonce_str(nonce_str);
String urlSign = SignatureUtil.createSign(payShortUrlParams, WechatConfig.API_KEY,
SystemConfig.DEFAULT_CHARACTER_ENCODING);
payShortUrlParams.setSign(urlSign);
String longXml = XmlUtil.toSplitXml(payShortUrlParams);
String shortResult = HttpReqUtil.HttpsDefaultExecute(SystemConfig.POST_METHOD, WechatConfig.PAY_SHORT_URL, null,
longXml);
PayShortUrlResult payShortUrlResult = XmlUtil.getObjectFromXML(shortResult, PayShortUrlResult.class);
if (Objects.equals("SUCCESS", payShortUrlResult.getReturn_code())) {
payurl = payShortUrlResult.getShort_url();
} else {
logger.debug("错误信息" + payShortUrlResult.getReturn_msg());
}
/**** 生成 二维码图片自行实现 ****/
return data;
}
/**
* 生成支付二维码URL
*
* @param params
* @return
*/
public static String createPayImageUrl(TreeMap<String, Object> params) {
StringBuffer buffer = new StringBuffer();
for (Map.Entry<String, Object> entry : params.entrySet()) {
if (entry.getValue() != null) {
buffer.append("&" + entry.getKey() + "=" + entry.getValue());
}
}
return buffer.toString();
}
三、回调商户支付URL
接收用户扫码后微信支付系统发送的数据,根据接收的数据生成商户系统的支付订单返回给微信支付系统,调用统一下单API提交支付交易
package com.phil.wechat.pay.controller;
import java.io.BufferedOutputStream;
import java.util.Objects;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.phil.modules.config.SystemConfig;
import com.phil.modules.config.WechatConfig;
import com.phil.modules.util.HttpReqUtil;
import com.phil.modules.util.PayUtil;
import com.phil.modules.util.SignatureUtil;
import com.phil.modules.util.XmlUtil;
import com.phil.wechat.base.controller.BaseController;
import com.phil.wechat.pay.model.rep.PayCallBackParams;
import com.phil.wechat.pay.model.rep.UnifiedOrderParams;
import com.phil.wechat.pay.model.resp.PayCallBackResult;
import com.phil.wechat.pay.model.resp.UnifiedOrderResult;
/**
* 扫码模式一回调
* @author phil
* @date 2017年6月27日
*/
@Controller
@RequestMapping("/wxpay/")
public class WechatPayCallBackController extends BaseController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping("/callback") // 仅仅是扫码模式一的
public void callBack() throws Exception {
String resXml = "";// 反馈给微信服务器
// 微信支付系统发送的数据(<![CDATA[product_001]]>格式)
String xml = HttpReqUtil.inputStreamToString(this.getRequest().getInputStream());
// logger.info("微信支付系统发送的数据"+xml);
/**** 微信支付系统发送的数据其实就是回调地址输入的参数Xml ****/
// 验证签名
if (SignatureUtil.checkIsSignValidFromWeiXin(xml, WechatConfig.API_KEY,
SystemConfig.DEFAULT_CHARACTER_ENCODING)) {
// 转换成输入参数,
PayCallBackParams payCallBackParams = XmlUtil.getObjectFromXML(xml, PayCallBackParams.class);
// appid openid mch_id is_subscribe nonce_str product_id sign
// 统一下单
String openid = payCallBackParams.getOpenid();
String product_id = payCallBackParams.getProduct_id();
/**** product_id 等 生成自己系统的订单 ****/
int total_fee = 1; // 根据product_id算出价格
String out_trade_no = PayUtil.createOutTradeNo(); // 生成订单号
String body = product_id; // 商品名称设置为product_id
String attach = "XXX店"; // 附加数据
String nonce_str = PayUtil.createNonceStr();
String spbill_create_ip = HttpReqUtil.getRemortIP(this.getRequest());
// 组装统一下单的请求参数
UnifiedOrderParams unifiedOrderParams = new UnifiedOrderParams();
unifiedOrderParams.setAppid(WechatConfig.APP_ID);// 必须
unifiedOrderParams.setMch_id(WechatConfig.MCH_ID);// 必须
unifiedOrderParams.setOut_trade_no(out_trade_no);
unifiedOrderParams.setBody(body);
unifiedOrderParams.setAttach(attach);
unifiedOrderParams.setTotal_fee(total_fee);// 必须
unifiedOrderParams.setNonce_str(nonce_str); // 必须
unifiedOrderParams.setSpbill_create_ip(spbill_create_ip); // 必须
unifiedOrderParams.setTrade_type("NATIVE");// 必须
unifiedOrderParams.setOpenid(openid);
unifiedOrderParams.setNotify_url(WechatConfig.NOTIFY_URL); // 异步通知URL
// 签名
String sign = SignatureUtil.createSign(unifiedOrderParams, WechatConfig.API_KEY,
SystemConfig.DEFAULT_CHARACTER_ENCODING);
unifiedOrderParams.setSign(sign);
// 统一下单 请求的Xml
String unifiedXmL = XmlUtil.toSplitXml(unifiedOrderParams);
// 统一下单 返回的xml
String unifiedOrderResultXmL = HttpReqUtil.HttpsDefaultExecute(SystemConfig.POST_METHOD,
WechatConfig.UNIFIED_ORDER_URL, null, unifiedXmL);
// 统一下单返回 验证签名
if (SignatureUtil.checkIsSignValidFromWeiXin(unifiedOrderResultXmL, WechatConfig.API_KEY,
SystemConfig.DEFAULT_CHARACTER_ENCODING)) {
UnifiedOrderResult unifiedOrderResult = (UnifiedOrderResult) XmlUtil
.getObjectFromXML(unifiedOrderResultXmL, UnifiedOrderResult.class);
if (Objects.equals("SUCCESS", unifiedOrderResult.getReturn_code())
&& Objects.equals("SUCCESS", unifiedOrderResult.getResult_code())) {
PayCallBackResult payCallBackResult = new PayCallBackResult();
payCallBackResult.setReturn_code(unifiedOrderResult.getReturn_code());
payCallBackResult.setAppid(WechatConfig.APP_ID);
payCallBackResult.setMch_id(WechatConfig.MCH_ID);
payCallBackResult.setNonce_str(unifiedOrderResult.getNonce_str());// 直接用微信返回的
/**** prepay_id 2小时内都有效,根据product_id再次支付方法自己写 ****/
payCallBackResult.setPrepay_id(unifiedOrderResult.getPrepay_id());
payCallBackResult.setResult_code(unifiedOrderResult.getResult_code());
String callsign = SignatureUtil.createSign(payCallBackResult, WechatConfig.API_KEY,
SystemConfig.DEFAULT_CHARACTER_ENCODING);
payCallBackResult.setSign(callsign);
resXml = XmlUtil.toXml(payCallBackResult).replace("__", "_");
// 将数据包返回给微信支付系统处理
}
} else {
logger.info("签名验证错误");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";
}
} else {
logger.info("签名验证错误");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";
}
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(this.getResponse().getOutputStream());
out.write(resXml.getBytes());
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(out);
}
}
}
四、完成支付并通知支付结果
关于源码,希望帮到了你