在微信小程序中玩家需要对NPC角色进行打赏,打赏的方式是通过微信支付来完成的 如何实现可参考微信支付开发文档 逻辑实现:
1、小程序获取用户相关openId,然后调用商户后台统一下单接口,换取预支付id
2、根据预支付id,小程序发起支付请求
3、提供微信支付通知接口给微信进行回调(此时可以对相关订单状态进行更新操作)
1、打赏效果图
2、前期准备
1、申请小程序,然后在小程序管理后台,申请接入微信支付
2、接入微信支付之后,配置支付相关秘钥以及下载商户证书
3、代码实现
1、根据微信支付官方文档提供的SDK进行开发
Gradle
在你的build.gradle
文件中加入如下的依赖
implementation 'com.github.wechatpay-apiv3:wechatpay-apache-httpclient:0.2.2'
Maven
加入以下依赖
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.2.2</version>
</dependency>
<!--2、如果需要用第三方库实现可添加以下依赖
此处借用了第三库的校验和响应返回工具类
参考文档:
-->
<dependency>
<groupId>com.egzosn</groupId>
<artifactId>pay-java-common</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>com.egzosn</groupId>
<artifactId>pay-java-wx</artifactId>
<version>2.12.1</version>
</dependency>
2、创建微信请求HttpClient工具类
public class WxHttpClient{
public static String appid = "XX"; // 小程序id
public static String mchId = "XX"; // 商户号
public static String mchSerialNo = "XX"; // 商户证书序列号
private static String apiV3Key = "XX"; // api密钥
public static String notify_url=""; //微信支付通知回调
private static volatile CloseableHttpClient wxHttpClient;
private static AutoUpdateCertificatesVerifier verifier;
private WxHttpClient(){}
public static CloseableHttpClient getWxHttpClient() {
if(wxHttpClient==null){
synchronized (WxHttpClient.class){
if(wxHttpClient==null){
//加载私钥
PrivateKey merchantPrivateKey = loadPrivateKey();
//使用自动更新的签名验证器,不需要传入证书
try {
verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
apiV3Key.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
System.out.println("构建签名验证器失败!!!");
}
//构建HttpClient
wxHttpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
}
}
}
return wxHttpClient;
}
public static PrivateKey loadPrivateKey(){
PrivateKey merchantPrivateKey = null;
try {
merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream("apiclient_key.pem"));
} catch (FileNotFoundException e) {
System.out.println(DateUtil.getCurrentDate()+"商户私钥加载异常");
e.printStackTrace();
}
return merchantPrivateKey;
}
//第三方工具类需要的配置
public static WxPayConfigStorage getWxPayConfigStorage(){
WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage();
wxPayConfigStorage.setMchId(mchId);//支付商户号
wxPayConfigStorage.setAppid(appid);//小程序appid
// wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填");
wxPayConfigStorage.setSecretKey("");// 商户支付密钥(之前设置的商户apiv3秘钥)
wxPayConfigStorage.setSignType("MD5");
wxPayConfigStorage.setInputCharset("utf-8");
return wxPayConfigStorage;
}
}
3、小程序调用下单及微信支付通知Controller
@RestController
@RequestMapping("/wxpay")
public class WxPayController {
@Resource
private UserService userService;
@Autowired
private WxPayService wxPayService;
@Autowired
private UserCreditService userCreditService;
private com.egzosn.pay.wx.api.WxPayService wxUserPayService=new com.egzosn.pay.wx.api.WxPayService(WxHttpClient.getWxPayConfigStorage());
/**
* @Desc 统一下单
* @Date 2021/5/22 13:38
*/
@RequestMapping("/unifiedOrder")
@SysLog(name="统一下单")
public ResultVO unifiedOrder(@RequestBody UserCredit userCredit) throws IOException {
//校验参数是否为空
BeanValidator.validate(userCredit, Creation.class);
//判断当前用户是否存在于系统中
User user=new User();
user.setId(userCredit.getUserId());
user.setOpenId(userCredit.getOpenId());
List<User> users = userService.selectAllData(user);
if(CollectionUtils.isEmpty(users)){
throw new BusinessException(0,"非系统用户,拒绝访问");
}
String outTradeNo=WxPayUtil.getOutTradeNo(userCredit.getUserId()+"");
userCredit.setAddTime(DateUtil.getCurrentDate());
userCredit.setOutTradeNo(outTradeNo);
userCredit.setTradeStatus(0);
//调用微信下单接口
String prepay_id_str = wxPayService.unifiedOrder(userCredit);
JSONObject rsobject=JSONObject.parseObject(prepay_id_str);
return ResultVOUtil.success(rsobject,"下单成功");
}
/**
*
* 微信用户支付回调接口
*/
@RequestMapping("/userPayCallBack")
public String wxUserPaycallback(HttpServletRequest request) {
try {
Map<String, Object> params = wxUserPayService.getParameter2Map(request.getParameterMap(),
request.getInputStream());
if (null == params) {
throw new Exception("回调参数为空");
}
// 校验
if (!wxUserPayService.verify(params)) {
throw new Exception("校验失败");
}
payDone(params);//支付处理
return wxUserPayService.getPayOutMessage("SUCCESS", "成功").toMessage();
} catch (Exception e) {
e.printStackTrace();
return wxUserPayService.getPayOutMessage("FALL", "失败").toMessage();
}
}
/**
* 支付结果处理
*/
private void payDone(Map<String, Object> dataMap) throws Exception {
System.out.println("支付回调:" + JSON.toJSONString(dataMap));
String result_code = dataMap.get("result_code").toString(); //支付结果code
String out_trade_no = dataMap.get("out_trade_no").toString(); //商户交易订单id,此为支付记录id
if (!result_code.equals("SUCCESS")) {
throw new Exception("支付回调失败:" + JSON.toJSONString(dataMap));
}
UserCredit userCredit=new UserCredit();
userCredit.setOutTradeNo(out_trade_no);
List<UserCredit> userCredits = userCreditService.selectAllData(userCredit);//获取打赏记录
if (CollectionUtils.isEmpty(userCredits)) {
throw new Exception("无支付记录,回调处理失败!!!");
}
userCredit.setTradeStatus(result_code.equals("SUCCESS") ? 1 : 2);
userCredit.setOutTradeNo(out_trade_no);
int updateFlag=userCreditService.updateCredit(userCredit);//根据回调结果更改支付结果状态
if(updateFlag==0){
throw new Exception("更新用户打赏表状态失败!!!");
}
}
}
4、创建微信支付服务类
@Service
public class WxPayServiceImpl implements WxPayService {
@Autowired
private UserCreditService userCreditService;
private CloseableHttpClient wxHttpClient=WxHttpClient.getWxHttpClient();
/**
* @Desc 统一下单接口
* @Date 2021/5/22 15:39
*/
@Override
public String unifiedOrder(UserCredit userCredit) throws IOException {
//添加微信下单记录
userCreditService.insertUserCredit(userCredit);
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type","application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("mchid", WxHttpClient.mchId)
.put("appid", WxHttpClient.appid)
.put("description", "Image形象店-深圳腾大-QQ公仔")
.put("notify_url", WxHttpClient.notify_url)
.put("out_trade_no", userCredit.getOutTradeNo());
rootNode.putObject("amount")
.put("total", userCredit.getAmount());
rootNode.putObject("payer")
.put("openid", userCredit.getOpenId());
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = wxHttpClient.execute(httpPost);
String prepay_id_str = EntityUtils.toString(response.getEntity());
JSONObject object=JSONObject.parseObject(prepay_id_str);
Object code = object.get("code");
if(code!=null){
if(code.toString().equals("PARAM_ERROR")){
throw new BusinessException(0,"下单失败:"+object.get("message"));
}
}
return prepay_id_str;
}
}