电商支付流程
支付过程涉及的角色
- 用户
- 商户
- 电商平台
- 微信
用户支付流程
- 下单
- 选择支付方式
- 扫码
- 支付
- 跳转订单页面
微信支付开发流程
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5
开发过程
- 生成订单
- 将订单插入平台数据库中(未支付状态)
- 在微信下单订单,微信返回付款url
- 将url包装到二维码
- 用户扫码,支付
- 微信收到钱后,通知平台,调用平台的接口
- 平台收到支付成功消息后,更新订单状态,修改库存
- 页面进行跳转
位置支付案例
下载微信支付的SDK
https://pay.weixin.qq.com/wiki/doc/api/download/WxPayAPI_JAVA.zip
配置微信支付
/**
* 微信支付配置
*/
public class MyWXConfig extends WXPayConfig {
//账户的APPID
public String getAppID() {
return "wx307113892f15a42e";
}
//商户ID
public String getMchID() {
return "1508236581";
}
//秘钥
public String getKey() {
return "HJd7sHGHd6djgdgFG5778GFfhghghgfg";
}
public InputStream getCertStream() {
return null;
}
public IWXPayDomain getWXPayDomain() {
return new WXPayDomain();
}
class WXPayDomain implements IWXPayDomain{
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
public DomainInfo getDomain(WXPayConfig config) {
return new DomainInfo("api.mch.weixin.qq.com",true);
}
}
}
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
下单、生成二维码
/**
* 微信支付Service
*/
@Service
public class PayService {
/**
* 下单
* @param goodsId 商品id
* @param price 价格
* @return 二维码的URL
*/
public Map<String, String> makeOrder(String goodsId, Long price) throws Exception {
//创建支付对象
MyWXPayConfig config = new MyWXPayConfig();
WXPay wxPay = new WXPay(config);
Map<String,String> map = new HashMap<>();
map.put("appid",config.getAppID());
map.put("mch_id",config.getMchID());
map.put("device_info","WEB");
map.put("nonce_str", UUID.randomUUID().toString().replace("-",""));
map.put("body","商城购物");
String tradeNo = UUID.randomUUID().toString().replace("-", "");
map.put("out_trade_no", tradeNo);
map.put("fee_type","CNY");
map.put("total_fee",String.valueOf(price));
map.put("notify_url","http://n538ep.natappfree.cc/order/notify"); //微信对商户后台的回调接口
map.put("trade_type","NATIVE");
map.put("product_id",goodsId);
//执行统一下单
Map<String, String> result = wxPay.unifiedOrder(map);
System.out.println("result:"+result);
//保存订单号
result.put("trade_no",tradeNo);
return result;
}
/**
* 生成二维码
* @param url
* @param response
*/
public void makeQRCode(String url, HttpServletResponse response){
//通过支付链接生成二维码
HashMap<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.MARGIN, 2);
try {
BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 200, 200, hints);
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", response.getOutputStream());
System.out.println("创建二维码完成");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 检查订单状态
* @param tradeNo
* @return
* @throws Exception
*/
public String checkOrder(String tradeNo) throws Exception {
MyWXPayConfig config = new MyWXPayConfig();
String str =
"<xml>"+
"<appid>"+config.getAppID()+"</appid>"+
"<mch_id>"+config.getMchID()+"</mch_id>"+
"<nonce_str>"+UUID.randomUUID().toString().replace("-","")+"</nonce_str>"+
"<out_trade_no>"+tradeNo+"</out_trade_no>"+
"<sign>5E00F9F72173C9449F802411E36208734B8138870ED3F66D8E2821D55B317078</sign>"+
"</xml>";
WXPay pay = new WXPay(config);
Map<String,String> map = WXPayUtil.xmlToMap(str);
Map<String, String> map2 = pay.orderQuery(map);
String state = map2.get("trade_state");
System.out.println("订单"+tradeNo+",状态"+state);
return state;
}
}
订单控制器
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private PayService payService;
private String tradeNo;
//二维码生成
@GetMapping("/code")
public void qrcode(@RequestParam("goodsId")String goodsId,
@RequestParam("price")Long price,
HttpServletResponse response){
try {
Map<String,String> map = payService.makeOrder(goodsId, price);
payService.makeQRCode(map.get("code_url"),response);
System.out.println("生成订单号:" + map.get("trade_no"));
tradeNo = map.get("trade_no");
} catch (Exception e) {
e.printStackTrace();
}
}
//支付后通知
@PostMapping("/notify")
public void notify(HttpServletRequest request,HttpServletResponse response) throws Exception {
//获得微信传来的xml字符串
String str = Streams.asString(request.getInputStream());
//将字符串xml转换为Map
Map<String, String> map = WXPayUtil.xmlToMap(str);
//读取订单号
String no = map.get("out_trade_no");
//模拟修改商户后台数据库订单状态
System.out.println("更新订单状态:"+no);
//给微信发送消息
response.getWriter().println("<xml>\n" +
" <return_code><![CDATA[SUCCESS]]></return_code>\n" +
" <return_msg><![CDATA[OK]]></return_msg>\n" +
" <appid><![CDATA["+map.get("appid")+"]]></appid>\n" +
" <mch_id><![CDATA["+map.get("mch_id")+"]]></mch_id>\n" +
" <nonce_str><![CDATA["+map.get("nonce_str")+"]]></nonce_str>\n" +
" <openid><![CDATA["+map.get("openid")+"]]></openid>\n" +
" <sign><![CDATA["+map.get("sign")+"]]></sign>\n" +
" <result_code><![CDATA[SUCCESS]]></result_code>\n" +
" <prepay_id><![CDATA["+map.get("prepay_id")+"]]></prepay_id>\n" +
" <trade_type><![CDATA[NATIVE]]></trade_type>\n" +
"</xml>");
}
//检查订单状态
@PostMapping("checkOrder")
public String checkOrder() throws Exception {
System.out.println("trade_no:" + tradeNo);
if(StringUtils.isEmpty(tradeNo)){
return null;
}
String success = payService.checkOrder(tradeNo);
System.out.println("check:" + success);
return success;
}
}
页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--二维码图片-->
<div id="app">
<img src="http://api.blb.com/api/order-api/order/code?goodsId=999&price=1">
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var interval;
//定时查询订单状态
interval = setInterval("checkOrder()",3000);
function checkOrder(){
axios.post("http://api.blb.com/api/order-api/order/checkOrder")
.then(res => {
console.log(res.data);
if(res.data === "SUCCESS"){
location.href = "success.html";
clearInterval(interval);
}
});
}
</script>
</body>
</html>