当前位置: 首页 > news >正文

Java实现微信native支付

Java实现微信native支付

1、介绍

Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。
详细介绍以及实现流程可参考官方文档:微信native支付官方指引文档,本文主要为编码者讲解具体实现细节。

2、业务流程

微信native支付的业务流程图
根据业务流程图可以得知,编码主要有两部分:
1. 调用统一下单API,将返回的预支付交易链接(code_url)生成二维码图片,供用户扫描;
2. 调用查询订单API,获取订单的支付状态;

3、实现逻辑

先说第一部分:调用统一下单API,将返回的预支付交易链接(code_url)生成二维码图片,供用户扫描
1、导入依赖

<dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-apache-httpclient</artifactId><version>0.4.9</version>
</dependency>

2、调用统一下单API

public class NativePayCreateOrder {// 一些参数,公司提供的,通常定义在项目的配置文件中,以下皆是模拟数据private appId = "wx8790234sau98232ehb2"; // 应用号private String mchId = "1571814339"; // 商户号private String privateKey = ""; // 私钥字符串,此处省略private String mchSerialNo = "25FS78SGSGFGG7879SGS987GS675AT6"; // 商户证书序列号private String apiV3Key = "CZBK12Y675AHIGA97987987957AD"; // V3密钥// 定义httpClient对象private CloseableHttpClient httpClient;@Beforepublic void setup() throws IOException {// 加载商户私钥(privateKey:私钥字符串)PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo,     merchantPrivateKey)),apiV3Key.getBytes("utf-8"));// 初始化httpClienthttpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey).withValidator(new WechatPay2Validator(verifier)).build();}@Afterpublic void after() throws IOException {httpClient.close();}
上述代码是初始化httpClient对象(就是将商户ID、商户序列号、商户私钥、
平台证书设置到httpClient对象中),
httpClient对象是用来发送请求的,向微信支付系统发送下单请求。public void CreateOrder() throws Exception{// 定义Post类型的http请求(参数是请求的地址,固定不变的,因为请求的是微信支付系统的地址)HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");// 定义请求body参数(后面要修改,其包含应用ID、商户号、订单金额等信息)String reqdata = "{"+ "\"time_expire\":\"2018-06-08T10:34:56+08:00\","+ "\"amount\": {"+ "\"total\":100,"+ "\"currency\":\"CNY\""+ "},"+ "\"mchid\":\"1230000109\","+ "\"description\":\"Image形象店-深圳腾大-QQ公仔\","+ "\"notify_url\":\"https://www.weixin.qq.com/wxpay/pay.php\","+ "\"out_trade_no\":\"1217752501201407033233368018\","+ "\"goods_tag\":\"WXG\","+ "\"appid\":\"wxd678efh567hg6787\","+ "\"attach\":\"自定义数据说明\","+ "\"detail\": {"+ "\"invoice_id\":\"wx123\","+ "\"goods_detail\": ["+ "{"+ "\"goods_name\":\"iPhoneX 256G\","+ "\"wechatpay_goods_id\":\"1001\","+ "\"quantity\":1,"+ "\"merchant_goods_id\":\"商品编码\","+ "\"unit_price\":828800"+ "},"+ "{"+ "\"goods_name\":\"iPhoneX 256G\","+ "\"wechatpay_goods_id\":\"1001\","+ "\"quantity\":1,"+ "\"merchant_goods_id\":\"商品编码\","+ "\"unit_price\":828800"+ "}"+ "],"+ "\"cost_price\":608800"+ "},"+ "\"scene_info\": {"+ "\"store_info\": {"+ "\"address\":\"广东省深圳市南山区科技中一道10000号\","+ "\"area_code\":\"440305\","+ "\"name\":\"腾讯大厦分店\","+ "\"id\":\"0001\""+ "},"+ "\"device_id\":\"013467007045764\","+ "\"payer_client_ip\":\"14.23.150.211\""+ "}"+ "}";StringEntity entity = new StringEntity(reqdata,"utf-8");entity.setContentType("application/json");httpPost.setEntity(entity);httpPost.setHeader("Accept", "application/json");//完成签名并执行请求(调用下单API)CloseableHttpResponse response = httpClient.execute(httpPost);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) { //处理成功System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));// 将上面的返回结果code_url返回(return)给前端,前端根据这个code_url生成二维码图片} else if (statusCode == 204) { //处理成功,无返回BodySystem.out.println("success");} else {System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));throw new IOException("request failed");}} finally {response.close();}}}

3.3、定义订单参数类

import lombok.Builder;
import lombok.Data;@Builder // 此注解的作用是可以通过builder()方法链式创建对象
@Data
public class NativePayParams {private String appid; // 应用idprivate String mchid; // 商户idprivate String description; // 商品描述private String out_trade_no; // 订单号private String notify_url; // 支付成功回调通知地址private Amount amount; // 订单金额信息
}

定义订单金额类

import lombok.Builder;
import lombok.Data;@Builder
@Data
public class Amount {private Integer total; // 金额,单位为分private String currency; // 货币类型,目前仅支持人民币
}

3.4、修改第二步中httpPost请求的body参数

// 定义请求body参数
Amount amount = Amount.builder().currency("CNY").total(1).build();
NativePayParams payParams = NativePayParams.builder().appid(appId).mchid(mchId).description("商品描述,如VIP特权").out_trade_no("ALJFKL7987987U98KJK").notify_url("https://项目的域名/native/notify") // 支付成功后通知的回调地址.amount(amount).build();	
String reqdata = JSON.toJSONString(payParams);

接下来,就是获取订单的支付状态,官网给了两种方案:一种是用户支付成功后,微信支付系统会将支付成功的结果以回调通知的形式同步给商户,商户的回调地址需要在调用Native下单API时传入notify_url参数;另一种获取订单支付状态的方式是当因网络抖动或本身notify_url存在问题等原因,导致无法接收到回调通知时,商户也可主动调用微信支付系统的查询订单API来获取订单状态

先说第一种方式的实现逻辑:用户支付成功后,微信支付系统会将支付成功的结果以回调通知的形式同步给商户,商户的回调地址需要在调用Native下单API时传入notify_url参数
1、编写接口,接收微信的支付成功通知
编写通知数据实体类

@Data
public class ResourceDto {private String algorithm;private String ciphertext;private String associated_data;private String original_type;private String nonce;
}

编写支付通知参数实体类

@Data
public class NotifyDto {private String id;private String create_time;private String event_type;private String resource_type;private ResourceDto resource;private String summary;
}

编写controller层接口

@RestController
@RequestMapping("/native")
public class NativePayController {@Autowiredprivate NativePayService nativePayService;@PostMapping("/notify")public Map<String, String> payNotify(@RequestBody NotifyDto dto){// 解密支付通知数据(调用业务逻辑层)return nativePayService.payNotify(dto);}
}

2、解密支付通知的内容
业务层接口

public interface NativePayService {Map<String, String> payNotify(NotifyDto dto);
}

业务层实现类

@Service
public class NativePayServiceImpl implements NativePayService {private String apiV3Key = "CZBK12Y675AHIGA97987987957AD"; // V3密钥@Overridepublic Map<String, String> payNotify(NotifyDto dto) {Map<String,String> res = null;try {// 解密微信支付系统传过来的参数String json = new AesUtil(apiV3Key.getBytes()).decryptToString(dto.getResource().getAssociated_data().getBytes(),dto.getResource().getNonce().getBytes(),dto.getResource().getCiphertext());String outTradeNo = JSON.parseObject(json, Map.class).get("out_trade_no").toString();System.out.println("支付成功的订单号:" + outTradeNo);} catch (GeneralSecurityException e) {// 支付不成功e.printStackTrace();res.put("code","FAIL");res.put("message","失败");}return res; // 返回正确的信息微信支付系统才不会继续发送通知}
}

3、内网穿透(本地测试的IP不是公网IP,外部无法访问,需要域名穿透,才能让微信支付系统回调到本地项目的接口)
可以选择用花生壳这个软件工具来做内容穿透,其实就是一个域名映射,映射到本地IP与项目端口;在实际开发中不需要做内网穿透,因为公司有服务器域名。

4、微信主动通知的地址是通过下单接口中的请求参数notify_url来设置的,要求必须是https地址

接下来说主动查询订单支付状态这种方式,因为微信支付系统不能保证通知成功,所以在企业开发中,都还会用这种方式来获取订单支付状态。本质上就是写接口向微信支付系统发请求。

以查询商户订单号为例:(还可以查询支付订单号,实现逻辑同理,可模仿调用下单API的接口编写)
public void queryOrder() throws Exception{// 定义Get类型的http请求HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/ALJFKL7987987U98KJK?mchid=1571814339");httpGet.setHeader("Accept", "application/json");//完成签名并执行请求(调用查询订单API)CloseableHttpResponse response = httpClient.execute(httpGet);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) { //处理成功System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));// 上面的返回结果就是json格式的订单信息,包括支付状态} else if (statusCode == 204) { //处理成功,无返回BodySystem.out.println("success");} else {System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));throw new IOException("request failed");}} finally {response.close();}
}

最后还有个问题,就是什么时候去调用刚编写的查询订单状态的这个接口;创建订单1分钟之后调用,用户可能还没支付,创建订单10分钟后调用,用户可能早就支付了,所以要用轮询的方式来调用查询订单状态这个接口,具体实现就是用定时任务,不会用定时任务的可以去看我之前的文章。

到这里其实就已经说完了这个微信支付的实现。下面就是扩展知识了,说一下SpringBoot的starter封装。比如说我们这里做好了微信扫码支付的功能,其他项目也要用这个功能,就不需要再实现这个功能了,直接在项目里引入这个starter就行了。这个SpringBoot的starter封装的原理以及具体使用方式留在下篇文章讲解。


http://www.mrgr.cn/news/45616.html

相关文章:

  • 钩子函数和回调函数
  • `pandas` 库提供了一个非常方便的方法将 DataFrame 转换为字典
  • JAVA开源项目 新生报到网站 计算机毕业设计
  • STM32-HAL库 驱动DS18B20温度传感器 -- 2024.10.8
  • Java的锁机制详解
  • 图像人脸与视频人脸匹配度检测
  • boost之第三方线程池
  • C高级--shell脚本实现分支判断
  • 简易STL实现 | Multiset 的实现
  • Badge插件的用法
  • Unite Shanghai 2024 团结引擎专场 | 团结引擎 OpenHarmony 工程剖析
  • 多线程编程的利器:C++线程锁深度解析
  • CMake
  • mysql游标的使用
  • 抖音小红书AI真人美女套图玩法,多种变现方式,手把手教你
  • APP自动化搭建与应用
  • 牛客 KY264 单词识别
  • 解析Vue源码中是如何进行模版编译的
  • 【代码随想录Day37】动态规划Part06
  • 电影《749局》路演 苗苗演绎超能力少女分享幕后故事