微信支付(H5)主要流程
1、用户打开页面,点击购买按钮
2、传递商品的id和价格到createCodeController
3、获得价格并搭配商户信息,发送给微信支付系统进行下单
4、返回支付链接到createCodeController,生成支付二维码
5、返回二维码到页面窗口,用户再进行扫码支付
6、间歇查询支付状态,传递订单号给checkOrderStatusController
7、获得订单号并搭配商户信息,发送到微信支付系统查询订单状态
8、返回当前订单支付状态到checkOrderStatusController,判断当前支付状态,向前端返回json信息
9、更新页面显示的支付状态信息
微信支付配置
public class PayConfig {
// 企业公众号 ID
public static String appid="wx************42e";
// 财付通平台的商户帐号
public static String partner="15******81";
// 财付通平台的商户密钥
public static String partnerKey="HJ******************gfg";
// 回调 URL
public static String notifyurl="你的回调地址";
}
微信支付配置模板(当前项目)
import com.github.wxpay.sdk.WXPayConfig;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
public class MyWXPayConfig implements WXPayConfig {
private String appID;
private String mchID;
private String key;
private InputStream certStream;
private String domain;
private int connectTimeoutMs;
private int readTimeoutMs;
private boolean useSandboxEnv;
public MyWXPayConfig(String appID, String mchID, String key, String certPath,
String domain, int connectTimeoutMs, int readTimeoutMs, boolean useSandboxEnv) throws Exception {
this.appID = appID;
this.mchID = mchID;
this.key = key;
this.domain = domain;
this.connectTimeoutMs = connectTimeoutMs;
this.readTimeoutMs = readTimeoutMs;
this.useSandboxEnv = useSandboxEnv;
if (certPath != null) {
this.certStream = new ByteArrayInputStream(readCertData(certPath));
}
}
private byte[] readCertData(String certPath) throws Exception {
// 根据certPath读取证书文件的内容,并返回字节数组形式
// 这里省略具体的读取过程,请根据实际情况进行实现
// 示例代码:
// FileInputStream fis = new FileInputStream(certPath);
// byte[] data = new byte[fis.available()];
// fis.read(data);
// fis.close();
// return data;
return null;
}
@Override
public String getAppID() {
return appID;
}
@Override
public String getMchID() {
return mchID;
}
@Override
public String getKey() {
return key;
}
@Override
public InputStream getCertStream() {
return certStream;
}
@Override
public int getHttpConnectTimeoutMs() {
return connectTimeoutMs;
}
@Override
public int getHttpReadTimeoutMs() {
return readTimeoutMs;
}
// @Override
// public boolean useSandbox() {
// return useSandboxEnv;
// }
//
// @Override
// public WXPayConstants.SignType getSignType() {
// return WXPayConstants.SignType.MD5;
// }
//
// @Override
// public String getDomain() {
// return domain;
// }
}
微信支付控制层
import com.edu.api.entity.PayOrder;
import com.edu.api.vo.Result;
import com.edu.pay.config.MyWXPayConfig;
import com.edu.pay.config.PayConfig;
import com.edu.pay.service.PayOrderService;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.jfinal.kit.HttpKit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@RestController
@RequestMapping("/pay")
public class PayController {
@Autowired
private PayOrderService payOrderService;
@PostMapping("/savePayOrder")
public Result savePayOrder(@RequestBody PayOrder payOrder){
return Result.ok(payOrderService.saveOrder(payOrder));
}
@PostMapping("/updatePayOrder")
public Result updatePayOrder(@RequestBody PayOrder payOrder){
return Result.ok(payOrderService.updateOrder(payOrder));
}
/**
* 创建支付二维码
* @param courseid
* @param coursename
* @param price
* @return
* @throws Exception
*/
@GetMapping("/createCode")
public Object createCode(String courseid,String coursename, String price) throws Exception {
//1.编写商户信息
Map<String,String> mm = new HashMap();
mm.put("appid", PayConfig.appid); //公众账号ID
mm.put("mch_id",PayConfig.partner);//商户号
mm.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
mm.put("body",coursename); //商品名称
String orderId = WXPayUtil.generateNonceStr();
System.out.println("订单编号 = "+orderId);
mm.put("out_trade_no",orderId); //商户订单号
mm.put("total_fee",price+""); //订单金额,单位分
mm.put("spbill_create_ip","127.0.0.1"); //终端IP
mm.put("notify_url",PayConfig.notifyurl); //通知地址
mm.put("trade_type","NATIVE"); //交易类型
System.out.println("发送的map = "+mm.toString());
//2.生成数字签名,并把上面的map转换成xml格式
String xml = WXPayUtil.generateSignedXml(mm,PayConfig.partnerKey);
System.out.println("转换后的xml = "+xml);
//3.将数据发送给微信后台,并得到微信后台返回的数据
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String result = HttpKit.post(url,xml);
System.out.println("返回的xml = "+result); //如果报错:<![CDATA[签名错误]]> 商户四要素的原因,重置商户API密钥。
//4.后台返回的xml格式,转成map,并添加两个参数
Map<String,String> resultMap = WXPayUtil.xmlToMap(result);
resultMap.put("orderId",orderId);
resultMap.put("money",price+"");
//5.将map返回给浏览器
return resultMap;
}
/**
* 关闭订单
* @param orderNo
* @return
* @throws Exception
*/
@GetMapping("/closeOrder")
public Boolean closeOrder(String orderNo) throws Exception {
WXPayConfig wxPayConfig = new MyWXPayConfig(PayConfig.appid, PayConfig.partner, PayConfig.partnerKey, null,
"api.mch.weixin.qq.com", 5000, 5000, false);
WXPay wxPay = new WXPay(wxPayConfig, WXPayConstants.SignType.MD5, false);
Map<String, String> respData;
Map<String, String> data = new TreeMap<>();
data.put("appid", PayConfig.appid);
data.put("mch_id",PayConfig.partner); // 商户号
data.put("out_trade_no", orderNo); // 商户订单号
data.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串小于32位
String sign = WXPayUtil.generateSignature(data, PayConfig.partnerKey);
data.put("sign", sign);
// 调用微信关闭订单接口
respData = wxPay.closeOrder(data);
if (respData.get("return_code").equals("SUCCESS")) {
System.out.println("关闭订单成功!");
return true;
}
return false;
}
/**
* 回调地址
* @param request
* @param response
* @return
* @throws IOException
*/
@RequestMapping("wxCallback")
public String wxCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
InputStream inStream = null;
ByteArrayOutputStream outSteam = null;
String resultxml = null;
try {
inStream = request.getInputStream();
outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
resultxml = new String(outSteam.toByteArray(), "utf-8");
} catch (Exception e) {
System.out.println("回调处理失败");
}finally {
if(null != outSteam) {
outSteam.close();
}
if(null != inStream) {
inStream.close();
}
}
System.out.println("wxCallback - 回调请求参数:"+ resultxml);
return resultxml;
}
/**
* 检查支付状态
* @param orderId
* @return
* @throws Exception
*/
@GetMapping("checkOrderStatus")
public Object checkOrderStatus(String orderId) throws Exception {
//1.编写商户信息
Map<String,String> mm = new HashMap();
mm.put("appid",PayConfig.appid); //公众账号ID
mm.put("mch_id",PayConfig.partner);//商户号
mm.put("out_trade_no",orderId);//订单编号
mm.put("nonce_str",WXPayUtil.generateNonceStr()); //随机字符串
System.out.println(mm);
//2.生成数字签名,并把上面的map转换成xml格式
String xml = WXPayUtil.generateSignedXml(mm, PayConfig.partnerKey);
System.out.println(xml);
//3.将数据发送给微信后台,并得到微信后台返回的数据
String url = "https://api.mch.weixin.qq.com/pay/orderquery";
//第一次询问时间
long beginTime = System.currentTimeMillis();
while(true) { //不停的去微信后台询问是否支付
String result = HttpKit.post(url, xml);
System.out.println(result);//报错:<![CDATA[签名错误]]>
//4.后台返回的xml格式,转成map,并添加两个参数
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
// 测试不支付
// resultMap.put("trade_state","success");
//5.将map转成json并返回给浏览器
//已经成功支付,停止询问
if(resultMap.get("trade_state").equalsIgnoreCase("success")){
// 测试不支付
// Thread.sleep(3000);
return resultMap;
}
//超过30秒未支付,停止询问
if(System.currentTimeMillis() - beginTime > 30000){
return resultMap;
}
Thread.sleep(3000); //每隔3秒,询问一次微信后台
}
}
}