基于springboot微信小程序支付功能实现
简单的封装微信小程序支付功能,支付工具类所依赖的fastjson、lombok、wagegger,
1、添加maven依赖:
版本号可根据自己项目的实际情况修改
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId></dependency>
2、创建支付基类BasePayEntity 实现 Serializable 接口:
required = true 表示必填字段
@Data@NoArgsConstructor@Accessors(chain = true)public class BasePayEntity implements Serializable {@JSONField(name = "appid")@ApiModelProperty(name = "appId" , required = true , value = "应用ID")protected String appId ;@JSONField(name = "mchid")@ApiModelProperty(name = "mchId" , required = true , value = "直连商户号")protected String mchId ;@JSONField(name = "description")@ApiModelProperty(name = "description" , required = true , value = "商品描述")protected String description ;@JSONField(name = "out_trade_no")@ApiModelProperty(name = "outTradeNo" , required = true , value = "商户订单号")protected String outTradeNo ;@JSONField(name = "time_expire")@ApiModelProperty(name = "timeExpire" , value = "交易结束时间")protected String timeExpire ;@JSONField(name = "attach")@ApiModelProperty(name = "attach", value = "附加数据")protected String attach ;@JSONField(name = "notify_url")@ApiModelProperty(name = "notifyUrl" , required = true , value = "通知地址")protected String notifyUrl ;@JSONField(name = "amount")@ApiModelProperty(name = "amount" , required = true , value = "订单金额")protected Amount amount ;@JSONField(name = "detail")@ApiModelProperty(name = "detail", value = "优惠功能")protected Detail detail ;@JSONField(name = "settle_info")@ApiModelProperty(name = "settleInfo" , value = "结算信息")protected SettleInfo settleInfo ;@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class Amount implements Serializable {@ApiModelProperty(name = "total" , required = true , value = "总金额")protected Integer total ;@ApiModelProperty(name = "currency" , required = true , value = "CNY:人民币,境内商户号仅支持人民币。")protected String currency ;}@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class Detail implements Serializable{@JSONField(name = "cost_price")@ApiModelProperty(name = "costPrice" , value = "总金额")protected Integer costPrice ;@JSONField(name = "invoice_id")@ApiModelProperty(name = "invoiceId" , value = "商品小票ID")protected String invoiceId ;}@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class SettleInfo implements Serializable{@JSONField(name = "profit_sharing")@ApiModelProperty(name = "profitSharing" , value = "是否指定分账")protected Boolean profitSharing ;}public BasePayEntity(String appId, String mchId, String description, String outTradeNo, String timeExpire, String attach, String notifyUrl, Amount amount, Detail detail, SettleInfo settleInfo) {this.appId = appId;this.mchId = mchId;this.description = description;this.outTradeNo = outTradeNo;this.timeExpire = timeExpire;this.attach = attach;this.notifyUrl = notifyUrl;this.amount = amount;this.detail = detail;this.settleInfo = settleInfo;}}
3、创建Jsapi下单实体类:
@Data@NoArgsConstructor@AllArgsConstructor@ApiModel("Jsapi下单")@Accessors(chain = true)@Url("https://api.mch./v3/pay/transactions/jsapi")public class JsApiPay extends BasePayEntity {@JSONField(name = "scene_info")@ApiModelProperty(name = "sceneInfo" , value = "支付场景描述")private SceneInfo sceneInfo ;@JSONField(name = "payer")@ApiModelProperty(name = "payer" , required = true , value = "支付者信息")private Payer payer ;@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class Payer implements Serializable{@ApiModelProperty(name = "openid" , required = true , value = "用户标识")private String openid ;}@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class SceneInfo implements Serializable{@JSONField(name = "payer_client_ip")@ApiModelProperty(name = "payerClientIp" , value = "用户的客户端IP,支持IPv4和IPv6两种格式的IP地址")private String payerClientIp ;@JSONField(name = "device_id")@ApiModelProperty(name = "deviceId" , value = "商户端设备号(门店号或收银设备ID)")private String deviceId ;@JSONField(name = "store_info")@ApiModelProperty(name = "storeInfo" , value = "商户门店信息")private StoreInfo storeInfo ;@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class StoreInfo implements Serializable{@JSONField(name = "id")@ApiModelProperty(name = "id" , value = "商户侧门店编号")private String id ;@JSONField(name = "name")@ApiModelProperty(name = "name" , value = "商户侧门店名称")private String name ;@JSONField(name = "area_code")@ApiModelProperty(name = "areaCode", value = "地区编码,详细请见省市区编号对照表。")private String areaCode ;@JSONField(name = "address")@ApiModelProperty(name = "address", value = "详细的商户门店地址")private String address ;}}@Builder(toBuilder = true)public JsApiPay(String appId, String mchId, String description, String outTradeNo, String timeExpire, String attach, String notifyUrl, Amount amount, Detail detail, SettleInfo settleInfo, SceneInfo sceneInfo, Payer payer) {super(appId, mchId, description, outTradeNo, timeExpire, attach, notifyUrl, amount, detail, settleInfo);this.sceneInfo = sceneInfo;this.payer = payer;}}
4、创建支付工具类:
注:BaseException为自定义异常类,WechatProperties 对应yml配置文件也可使用其他的方式传递配置,SignUtils签名,
public class MiniPayUtils {private WechatProperties wechatProperties ;public static MiniPayUtils newInstance(WechatProperties wechatProperties){MiniPayUtils miniPayUtils = new MiniPayUtils();miniPayUtils.wechatProperties = wechatProperties ;return miniPayUtils ;}public MiniPayResult pay(JsApiPay jsApiPay) {BasePayEntity.Amount amount = jsApiPay.getAmount();jsApiPay = jsApiPay.toBuilder().appId(wechatProperties.getMini().getAppid()).amount(ObjectUtils.isEmpty(amount) ? BasePayEntity.Amount.builder().currency("CNY").build() : amount.setCurrency("CNY")).notifyUrl(wechatProperties.getNotifyUrl()).mchId(wechatProperties.getMerchant().getMchId()).build();String nonceStr = SignUtils.getRandom() , timeStamp = String.valueOf(System.currentTimeMillis() / 1000) ;MiniPayResult miniPayResult = MiniPayResult.builder().appId(jsApiPay.getAppId()).timeStamp(timeStamp).nonceStr(nonceStr).packages(newInstance(wechatProperties).prepayId(jsApiPay , nonceStr , timeStamp)).signType("RSA").build();miniPayResult.setPaySign(SignUtils.sign(miniPayResult.toJsonString().getBytes() , wechatProperties.getMerchant().getCertPath() , ProjectStatus.windows操作系统.getValue().equals(wechatProperties.getEnvironment())));return miniPayResult ;}public String prepayId(JsApiPay jsapiPay , String nonceStr , String timeStamp){try{String url = jsapiPay.getClass().getAnnotation(Url.class).value() , body = JSONObject.toJSONString(jsapiPay);HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.add(HttpHeaders.AUTHORIZATION , SignUtils.getToken(HttpMethod.POST.name(), new URL(url), body, nonceStr, timeStamp, jsapiPay.getMchId(), wechatProperties.getMerchant().getSerialNo(), wechatProperties.getMerchant().getCertPath(), ProjectStatus.windows操作系统.getValue().equals(wechatProperties.getEnvironment())));httpHeaders.add(HttpHeaders.CONTENT_TYPE , MediaType.APPLICATION_JSON_VALUE);ResponseEntity<String> exchange = getRestTemplate().exchange(url , HttpMethod.POST , new HttpEntity(body , httpHeaders), String.class );if(HttpStatus.OK.equals(exchange.getStatusCode())) {JSONObject jsonObject = JSONObject.parseObject(exchange.getBody());return "prepay_id=" + jsonObject.getString("prepay_id");}throw BaseException.throwEx(JSONObject.parseObject(exchange.getBody()).getString("message"));}catch (Exception e){String message = e.getMessage();if(e instanceof HttpClientErrorException){HttpClientErrorException exception = (HttpClientErrorException)e;if(403 == exception.getRawStatusCode()) throw BaseException.throwEx(JSONObject.parseObject(message.substring(message.indexOf("[") + 1 , message.lastIndexOf("]"))).getString("message"));}throw BaseException.throwEx(message);}}}