目录
前言
本文章使用的是v3的版本,使用微信支付推荐的sdk包:wechatpay-java(有很多工具方法,使用非常简单方便)
准备工作
微信方的数据:(必须)
+ 商户号
+ 商户api私钥
+ 商户apiv3密钥
+ 商户证书序列号
+ 公众号id,小程序id等(和商户绑定的端,根据使用的支付方式来决定)
如果没有域名,需要使用内网穿透(用来做回调的,支付的回调不需要在微信那边配置,生成订单是指定回调url)
引入微信支付的jar包
方式一:(本文章使用的这个jar包)
这个jar包,其中提供了大量工具类,我们不需要在去在意签名解密,设置访问的url等操作,直接使用他提供的方法即可,操作简单方便
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.16</version>
</dependency>
方式二:
这个jar包,属于基础的jar包,属于手动的去设置签名解密,设置访问的url等,操作会更加细粒化,但初次使用比较麻烦
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</dependency>
支付的实现
介绍
微信支付提供的常见支付方式
+ native支付:这个就是我们常见的二维码扫码支付
+ jsapi支付:在微信内使用的支付,通常用在公众号和小程序直接调用支付的方式
+ h5支付:是在手机浏览器中实现的支付,通常确定订单后后跳转到微信支付页
+ app支付:使用在手机app上的支付,通常也是确定订单后跳转到微信支付页
+ 付款码支付(就是别人扫你的那种,只有v2的方式,暂时没有v3的)
等
使用wechatpay-java的jar包写代码的话,他们的native支付,jsapi支付,h5支付,app支付,在(预订单,查询订单,关闭订单)代码上基本一致,但引用的包,使用的类,传递的参数会有区别需要注意(重点),其中(支付回调,退款回调,关单)代码一摸一样的
预支付的实现(回调在后面)
公共部分
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
public String pay() {
String merchantId = "商户号";
String privateKeyPath = "商户api私钥";
String apiV3key = "商户apiv3密钥";
String merchantSerialNumber = "商户证书序列号";
String appId = "公众号id,小程序id等(和商户绑定的端,根据使用的支付方式来决定)";
String notifyUrl = "支付回调地址";
//这部分和上面的一部分可以提取出去,因为微信支付都会使用这个config
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
/*
你自身的业务逻辑,如生成订单,查询该订单是否过期等等
*/
//模拟数据
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
String orderId = String.valueOf(snowflake.nextId());//订单(字符串类型,保证唯一即可,我这里使用的hutool工具类的雪花算法,需要引入hutool的jar包)
Integer price = 1;//价格,以分为单位
String commoditiesName = "这是一个测试商品jsapi"; //商品名称127个字符
//todo 这里定义的就是各种支付方式的不同的代码
catch (HttpException e) {
log.error("微信下单发送HTTP请求失败,错误信息:{}", e.getHttpRequest());
throw new RuntimeException("微信下单发送HTTP请求失败", e);
} catch (ServiceException e) {
// 服务返回状态小于200或大于等于300,例如500
log.error("微信下单服务状态错误,错误信息:{}", e.getErrorMessage());
throw new RuntimeException("微信下单服务状态错误", e);
} catch (MalformedMessageException e) {
// 服务返回成功,返回体类型不合法,或者解析返回体失败
log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
throw new RuntimeException("服务返回成功,返回体类型不合法,或者解析返回体失败", e);
}
}
native支付(放到公共部分)
native用到的类(包是带有native的)
import com.wechat.pay.java.service.payments.nativepay.model.*;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
native的代码(使用的类不同,包不同,传递的参数不同)
// 构建service
NativePayService service = new NativePayService.Builder().config(config).build();
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(price);
request.setAmount(amount);
request.setAppid(appId);//APPID是微信开放平台(移动应用)或微信公众平台(小程序、公众号)为开发者的应用程序提供的唯一标识
request.setMchid(merchantId);//商户号
request.setDescription(commoditiesName);//商品名称,不能超过127字符
request.setNotifyUrl(notifyUrl);//回调的地址
request.setOutTradeNo(orderId);//订单号,一定要是唯一的
// 调用下单方法,得到应答
try {
PrepayResponse response = service.prepay(request);
return response.getPrepayId();//这个值要返回给前端,让前端调用微信的支付确定页面
}
jsapi支付(放到公共部分)
jsapi用到的类(包是带有jsapi的)
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
jsapi的代码(使用的类不同,包不同,传递的参数不同)
// 构建service
JsapiService service = new JsapiService.Builder().config(config).build();
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(price);
request.setAmount(amount);
request.setAppid(appId);//APPID是微信开放平台(移动应用)或微信公众平台(小程序、公众号)为开发者的应用程序提供的唯一标识
request.setMchid(merchantId);//商户号
request.setDescription(commoditiesName);//商品名称,不能超过127字符
request.setNotifyUrl(notifyUrl);//回调的地址
request.setOutTradeNo(orderId);//订单号,一定要是唯一的
Payer payer = new Payer();
payer.setOpenid("xxxxx");//小程序的用户的openid
// 调用下单方法,得到应答
try {
PrepayResponse response = service.prepay(request);
return response.getPrepayId();//这个值要返回给前端,让前端调用微信的支付确定页面
}
app支付(放到公共部分)
app用到的类(包是带有app的)
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.app.model.*;
app的代码(使用的类不同,包不同,传递的参数不同)
// 构建service
AppService service = new AppService.Builder().config(config).build();
// request.setXxx(val)设置所需参数,具体参数可见Request定义
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(price);
request.setAmount(amount);
request.setAppid(appId);//APPID是微信开放平台(移动应用)或微信公众平台(小程序、公众号)为开发者的应用程序提供的唯一标识
request.setMchid(merchantId);//商户号
request.setDescription(commoditiesName);//商品名称,不能超过127字符
request.setNotifyUrl(notifyUrl);//回调的地址
request.setOutTradeNo(orderId);//订单号,一定要是唯一的
// 调用下单方法,得到应答
try {
PrepayResponse response = service.prepay(request);
return response.getPrepayId();//这个值要返回给前端,让前端调用微信的支付确定页面
}
h5支付(放到公共部分)
h5用到的类(包是带有h5的)
import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.h5.model.*;
h5的代码(使用的类不同,包不同,传递的参数不同)
// 构建service
H5Service service = new H5Service.Builder().config(config).build();
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(price);
request.setAmount(amount);
request.setAppid(appId);//APPID是微信开放平台(移动应用)或微信公众平台(小程序、公众号)为开发者的应用程序提供的唯一标识
request.setMchid(merchantId);//商户号
request.setDescription(commoditiesName);//商品名称,不能超过127字符
request.setNotifyUrl(notifyUrl);//回调的地址
request.setOutTradeNo(orderId);//订单号,一定要是唯一的//
SceneInfo sceneInfo = new SceneInfo();
sceneInfo.setPayerClientIp("xxxxxx");//手机端的ip,支持ip4和ip6,一定要和手机ip复合
H5Info h5Info = new H5Info();
h5Info.setType("Android");//使用的场景 Wap,ios,android
sceneInfo.setH5Info(h5Info);
request.setSceneInfo(sceneInfo);
// 调用下单方法,得到应答
try {
PrepayResponse response = service.prepay(request);
return response.getPrepayId();//这个值要返回给前端,让前端调用微信的支付确定页面
}
查询项目状态
公共部分
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
public Transaction orderPayDateil(){
String merchantId = "商户号";
String privateKeyPath = "商户api私钥";
String apiV3key = "商户apiv3密钥";
String merchantSerialNumber = "商户证书序列号";
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
//todo 这里定义的就是各种支付方式的不同的代码
try {
Transaction transaction = service.queryOrderByOutTradeNo(request);//注意是你使用的支付方式的包,不要引用错了
log.info(transaction.toString());
return transaction;
} catch (ServiceException e) {
log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
throw new RuntimeException("订单查询失败", e);
}
}
native支付(放到公共部分)
native用到的类(包是带有native的)
import com.wechat.pay.java.service.payments.nativepay.model.*;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
native的代码(使用的类不同,包不同,传递的参数不同)
NativePayService service = new NativePayService.Builder().config(config).build();
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo("xxx");//订单号
request.setMchid(merchantId);//商户号
jsapi支付(放到公共部分)
jsapi用到的类(包是带有jsapi的)
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
jsapi的代码(使用的类不同,包不同,传递的参数不同)
JsapiService service = new JsapiService.Builder().config(config).build();
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo("xxx");//订单号
request.setMchid(merchantId);//商户号
app支付(放到公共部分)
app用到的类(包是带有app的)
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.app.model.*;
app的代码(使用的类不同,包不同,传递的参数不同)
AppService service = new AppService.Builder().config(config).build();
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo("xxxxx");//订单号
request.setMchid(merchantId);//商户号
h5支付(放到公共部分)
h5用到的类(包是带有h5的)
import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.h5.model.*;
h5的代码(使用的类不同,包不同,传递的参数不同)
H5Service service = new H5Service.Builder().config(config).build();
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo("xxxxxx");//订单号
request.setMchid(merchantId);//商户号
关闭订单
公共部分
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
public void closeOrder(){
String merchantId = "商户号";
String privateKeyPath = "商户api私钥";
String apiV3key = "商户apiv3密钥";
String merchantSerialNumber = "商户证书序列号";
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
//todo 这里定义的就是各种支付方式的不同的代码
try {
//方法没有返回值,意味着成功时API返回204 No Content
service.closeOrder(closeRequest);
log.info("关单成功");
} catch (ServiceException e) {
log.error("订单关闭失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
throw new RuntimeException("订单关闭失败", e);
}
}
native支付(放到公共部分)
native用到的类(包是带有native的)
import com.wechat.pay.java.service.payments.nativepay.model.*;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
native的代码(使用的类不同,包不同,传递的参数不同)
NativePayService service = new NativePayService.Builder().config(config).build();
CloseOrderRequest closeRequest = new CloseOrderRequest();
closeRequest.setMchid(merchantId);//商户号
closeRequest.setOutTradeNo("xxx");//订单id
jsapi支付(放到公共部分)
jsapi用到的类(包是带有jsapi的)
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
jsapi的代码(使用的类不同,包不同,传递的参数不同)
JsapiService service = new JsapiService.Builder().config(config).build();
CloseOrderRequest closeRequest = new CloseOrderRequest();
closeRequest.setMchid(merchantId);//商户号
closeRequest.setOutTradeNo("xxxx"); //订单号
app支付(放到公共部分)
app用到的类(包是带有app的)
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.app.model.*;
app的代码(使用的类不同,包不同,传递的参数不同)
AppService service = new AppService.Builder().config(config).build();
CloseOrderRequest closeRequest = new CloseOrderRequest();
closeRequest.setMchid(merchantId);//商户号
closeRequest.setOutTradeNo("xxxx");//订单号
h5支付(放到公共部分)
h5用到的类(包是带有h5的)
import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.h5.model.*;
h5的代码(使用的类不同,包不同,传递的参数不同)
H5Service service = new H5Service.Builder().config(config).build();
CloseOrderRequest closeRequest = new CloseOrderRequest();
closeRequest.setMchid(merchantId);//商户号
closeRequest.setOutTradeNo("xxxxx"); //订单id
预支付的回调
几种支付方式的回调是一样的
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
public String ncallBack(HttpServletRequest request, HttpServletResponse response){
String merchantId = "商户号";
String privateKeyPath = "商户api私钥";
String apiV3key = "商户apiv3密钥";
String merchantSerialNumber = "商户证书序列号";
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
try {
// 从请求头中获取信息
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String singType = request.getHeader("Wechatpay-Signature-Type");
String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
//获取body中的数据
String requestBody2 = IOUtils.toString(request.getReader());//需要用到ioutils工具类
String requestBody = requestBody2.toString();
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(wechatPayCertificateSerialNumber)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.signType(singType)
.body(requestBody)
.build();
// 初始化解析器 NotificationParser
NotificationParser parser = new NotificationParser((NotificationConfig) config);
Transaction decryptObject = parser.parse(requestParam, Transaction.class);
String tradeState = decryptObject.getTradeState().toString();
String outTradeNo = decryptObject.getOutTradeNo();
if ("SUCCESS".equals(tradeState)) {
/*
支付成功的处理(你自己的业务)
*/
System.out.println("订单 " + outTradeNo + " 支付成功");
// 这里可以添加更新订单状态到数据库等操作
} else {
/*
支付失败的处理(你自己的业务)
*/
System.out.println("订单 " + outTradeNo + " 支付失败,状态:" + tradeState);
}
// 返回成功响应给微信
return "{\"code\":\"SUCCESS\",\"message\":\"成功\"}";
} catch (Exception e) {
// 签名验证失败或解析异常,返回失败响应给微信
System.out.println("回调处理失败:" + e.getMessage());
return "{\"code\":\"FAIL\",\"message\":\"失败\"}";
}
}
退款功能
几种支付方式的回调是一样的
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
public Refund refund() {
String merchantId = "商户号";
String privateKeyPath = "商户api私钥";
String apiV3key = "商户apiv3密钥";
String merchantSerialNumber = "商户证书序列号";
String notifyUrl = "支付回调地址";
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
//构建退款service
RefundService service = new RefundService.Builder().config(config).build();
CreateRequest request = new CreateRequest();
//构建订单金额信息
AmountReq amountReq = new AmountReq();
//退款金额
Long num = 1l;
amountReq.setRefund(num);
//原订单金额
amountReq.setTotal(num);
//货币类型(默认人民币)
amountReq.setCurrency("CNY");
request.setAmount(amountReq);
//商户退款单号
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
String orderId = String.valueOf(snowflake.nextId());//使用hutool工具类(引入hutool的jar包),创建雪花算法的id,这个就是我们的订单号(字符串类型,可以自己定义,但要保证唯一)
request.setOutRefundNo(orderId);//退款新生成的单号
//商户订单号
request.setOutTradeNo("xxxx");//原订单id
//退款通知回调地址
request.setNotifyUrl(notifyUrl);
try {
/*
退款逻辑
*/
log.info("===========退款申请成功");
Refund refund = service.create(request);
return refund;
} catch (ServiceException e) {
log.error("退款申请作失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
throw new RuntimeException("退款申请失败", e);
}
}
退款回调
几种支付方式的回调是一样的
public String refundCallBack(HttpServletRequest request, HttpServletResponse response){
String merchantId = "商户号";
String privateKeyPath = "商户api私钥";
String apiV3key = "商户apiv3密钥";
String merchantSerialNumber = "商户证书序列号";;
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
try {
// 从请求头中获取信息
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String singType = request.getHeader("Wechatpay-Signature-Type");
String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
//获取body中的数据
String requestBody2 = IOUtils.toString(request.getReader());//需要用到ioutils工具类
String requestBody = requestBody2.toString();
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(wechatPayCertificateSerialNumber)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.signType(singType)
.body(requestBody)
.build();
// 初始化解析器 NotificationParser
NotificationParser parser = new NotificationParser((NotificationConfig) config);
RefundNotification refundNotification = parser.parse(requestParam, RefundNotification.class);
String refundStatus = refundNotification.getRefundStatus().toString();
String outTradeNo = refundNotification.getOutTradeNo();
if ("SUCCESS".equals(refundStatus)) {
/*
退款回调逻辑
*/
System.out.println("订单 " + outTradeNo + " 退款成功");
// 这里可以添加更新订单状态到数据库等操作
} else {
// 退款失败,处理失败逻辑
System.out.println("订单 " + outTradeNo + " 退款失败,状态:" + refundStatus);
}
// 返回成功响应给微信
return "{\"code\":\"SUCCESS\",\"message\":\"成功\"}";
} catch (Exception e) {
// 签名验证失败或解析异常,返回失败响应给微信
System.out.println("回调处理失败:" + e.getMessage());
return "{\"code\":\"FAIL\",\"message\":\"失败\"}";
}
}
无用(自己记录一下)
除了支付的其他功能
看微信的支付文档,发现v3还提供了很多功能,记录一下,以后用到在打笔记
+ 商家转账功能:可以用来实现向用户转账,比如用户提现佣金自动到账,比如可以完成任务向用户转账等功能
+ 微信还提供支付分的功能(根据支付分来确定是否免押金等)
+ 代金卷功能(用来创建卷):可以给客户发送代金卷,然后可以使用微信支付,会用代金卷抵消金额(相当于商家帮客户付了一部分)
+ 商家卷功能(用来创建卷):自己商家的优惠卷,可以在客户的微信优惠卷中显示(比只单纯在商家自己的公众号中显示更好),并提示过期时间和过期提醒等
+ 小程序发卷,h5发卷((用来发卷):两种发卷方式,用来发放上面的代金卷和商家卷
+ 微信红包的功能:营销手段,向客户发送红包,客户24小时不领取会退回(使用的v2接口)
+ 支付即服务功能:可以在支付订单处,显示商户信息,让客户可以快速添加商户进行强关联
+ 电子发票:可以给客户开电子发票到客户卡包
+ 支付有礼:用于实体店,支付完成后发送卷
+ 智慧商圈:就是一个针对商圈的解决方案,客户可以开通,开通后可以在不同的加盟商户中,共享一些内容,比如积分
+ 支付分停车服务(对接支付分和车牌实现的,主要用在停车场,用不到吧)
委派营销(好像是和直播合作,从而发放卷的功能)
总结
需要开发人员完成的功能:商家转账、代金券发放、商家券发放、小程序/H5发券、微信红包、支付即服务、电子发票、支付有礼、智慧商圈、支付分停车服务、委派营销等。
可以通过管理人员配置的功能:支付分、代金券/商家券的基本配置、电子发票的基本配置、支付有礼的基本配置、智慧商圈的基本配置等。
一些自己的理解
+ 不同的订单方式(根据业务选定)
1.只有一个订单,点击多次使用同一个订单(不过期的情况),可以更好的防止重复支付的问题
2.多个订单,每次点击都会生成新的订单,会出现重复支付的问题(必须要解决)
+ 支付的实现:通常都是先一个接口生成或查询订单,然后一个接口用来预支付