Commit d9b574a0 authored by shiyu's avatar shiyu

支付接口

parent 1651acbb
......@@ -70,6 +70,19 @@
<artifactId>qiniu-java-sdk</artifactId>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
......
package com.wwdz.ch.core.api.wxpay;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WxPayAutoCertificateConfig {
@Autowired
WxPayParamConfig wxPayParamConfig;
/**
* 初始化商户配置
* @return
*/
@Bean
public RSAAutoCertificateConfig rsaAutoCertificateConfig() {
RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(wxPayParamConfig.getMerchantId())
.privateKey(wxPayParamConfig.getPrivateKey())
.merchantSerialNumber(wxPayParamConfig.getMerchantSerialNumber())
.apiV3Key(wxPayParamConfig.getApiV3Key())
.build();
return config;
}
}
\ No newline at end of file
package com.wwdz.ch.core.api.wxpay;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "dts.wx-pay")
public class WxPayParamConfig {
/**
* APPID
*/
private String appId;
/**
* mchid
*/
private String merchantId;
/**
* 商户API私钥
*/
private String privateKey;
/**
* 商户证书序列号
*/
private String merchantSerialNumber;
/**
* 商户APIv3密钥
*/
private String apiV3Key;
/**
* 支付通知地址
*/
private String payNotifyUrl;
/**
* 私钥地址
*/
private String privateKeyPath;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getMerchantId() {
return merchantId;
}
public void setMerchantId(String merchantId) {
this.merchantId = merchantId;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
public String getMerchantSerialNumber() {
return merchantSerialNumber;
}
public void setMerchantSerialNumber(String merchantSerialNumber) {
this.merchantSerialNumber = merchantSerialNumber;
}
public String getApiV3Key() {
return apiV3Key;
}
public void setApiV3Key(String apiV3Key) {
this.apiV3Key = apiV3Key;
}
public String getPayNotifyUrl() {
return payNotifyUrl;
}
public void setPayNotifyUrl(String payNotifyUrl) {
this.payNotifyUrl = payNotifyUrl;
}
public String getPrivateKeyPath() {
return privateKeyPath;
}
public void setPrivateKeyPath(String privateKeyPath) {
this.privateKeyPath = privateKeyPath;
}
}
package com.wwdz.ch.core.api.wxpay;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
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.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wwdz.ch.core.type.Result;
import com.wwdz.ch.db.dto.request.distribution.DistributionOrderRequestDto;
import com.xxdxxs.utils.JsonUtils;
import com.xxdxxs.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.*;
import java.util.Base64;
@Component
public class WxPayServiceApi {
private static final Logger logger = LoggerFactory.getLogger(WxPayServiceApi.class);
@Autowired
WxPayParamConfig wxPayParamConfig;
@Autowired
RSAAutoCertificateConfig rsaAutoCertificateConfig;
/**
* 创建预支付订单
* @param dto
* @return
*/
public Result createPrepareOrder(DistributionOrderRequestDto dto) {
//请求微信支付相关配置
JsapiServiceExtension service = new JsapiServiceExtension.Builder()
.config(rsaAutoCertificateConfig)
.signType("RSA") // 不填默认为RSA
.build();
PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
try {
PrepayRequest request = new PrepayRequest();
request.setAppid(wxPayParamConfig.getAppId());
request.setMchid(wxPayParamConfig.getMerchantId());
//商品描述
request.setDescription(dto.getItemName());
//商户订单号
request.setOutTradeNo(dto.getDistributionOrderId());
request.setNotifyUrl(wxPayParamConfig.getPayNotifyUrl());
Amount amount = new Amount();
//单位为分,参数中的amount需要乘以过100
amount.setTotal(new BigDecimal(dto.getAmount()).intValue());
request.setAmount(amount);
Payer payer = new Payer();
//用户在直连商户appid下的唯一标识
payer.setOpenid(dto.getBuyerOpenid());
request.setPayer(payer);
logger.info("请求预支付下单,请求参数:{}", JsonUtils.from(request));
// 调用预下单接口
response = service.prepayWithRequestPayment(request);
logger.info("订单【{}】发起预支付成功,返回信息:{}", dto.getDistributionOrderId(), response);
return Result.success();
} catch (HttpException e) { // 发送HTTP请求失败
logger.error("微信下单发送HTTP请求失败,错误信息:{}", e.getHttpRequest());
} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
logger.error("微信下单服务状态错误,错误信息:{}", e.getErrorMessage());
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
logger.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
}
return Result.failed("下单失败");
}
/**
* 根据支付订单号查询订单
* @param dto
* @return
*/
public Result queryOrderByTransactionId(DistributionOrderRequestDto dto) {
QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
queryRequest.setMchid(wxPayParamConfig.getMerchantId());
queryRequest.setTransactionId(dto.getPaymentId());
try {
JsapiServiceExtension service =
new JsapiServiceExtension.Builder()
.config(rsaAutoCertificateConfig)
.signType("RSA") // 不填默认为RSA
.build();
Transaction result = service.queryOrderById(queryRequest);
if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
logger.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
}
return Result.success();
} catch (ServiceException e) {
logger.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
return Result.failed();
}
}
/**
* 根据商户订单号查询订单
* @param dto
* @return
*/
public Result<Object> queryOrderBytOutTradeNo(DistributionOrderRequestDto dto) {
QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
queryRequest.setMchid(wxPayParamConfig.getMerchantId());
queryRequest.setOutTradeNo(dto.getDistributionOrderId());
try {
JsapiServiceExtension service =
new JsapiServiceExtension.Builder()
.config(rsaAutoCertificateConfig)
.signType("RSA") // 不填默认为RSA
.build();
Transaction result = service.queryOrderByOutTradeNo(queryRequest);
if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
logger.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
}
return Result.success();
} catch (ServiceException e) {
logger.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
return Result.failed();
}
}
/**
* 微信调起支付参数
* https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml
*
* @param prepayId 微信下单返回的prepay_id
* appId 应用ID
* mch_id 商户号
* private_key_path 私钥路径
* @return 当前调起支付所需的参数
* @throws Exception
*/
public Result wxAppPayTuneUp(String prepayId) {
try {
if (StringUtils.hasLength(prepayId)) {
long timestamp = System.currentTimeMillis() / 1000;
String nonceStr = generateNonceStr();
//加载签名
String packageSign = sign(buildMessage(wxPayParamConfig.getAppId(), timestamp, nonceStr, prepayId).getBytes(), wxPayParamConfig.getPrivateKeyPath());
JSONObject jsonObject = new JSONObject();
jsonObject.put("appId", wxPayParamConfig.getAppId());
jsonObject.put("prepayId", prepayId);
jsonObject.put("timeStamp", timestamp);
jsonObject.put("nonceStr", nonceStr);
jsonObject.put("package", "Sign=WXPay");
jsonObject.put("signType", "RSA");
jsonObject.put("sign", packageSign);
jsonObject.put("partnerId", wxPayParamConfig.getMerchantId());
return Result.success(jsonObject);
}
} catch (Exception e) {
logger.error("生成签名错误 error : {}", e);
}
return Result.failed();
}
public String sign(byte[] message, String private_key_path) throws SignatureException, IOException, NoSuchAlgorithmException, InvalidKeyException {
//签名方式
Signature sign = Signature.getInstance("SHA256withRSA");
//私钥
sign.initSign(PemUtil.loadPrivateKey(new ClassPathResource(private_key_path).getInputStream()));
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 按照前端签名文档规范进行排序,\n是换行
*
* @param appId appId
* @param timestamp 时间
* @param nonceStr 随机字符串
* @param prepay_id prepay_id
* @return
*/
public String buildMessage(String appId, long timestamp, String nonceStr, String prepay_id) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ prepay_id + "\n";
}
//生成随机字符串 微信底层的方法,直接copy出来了
protected String generateNonceStr() {
SecureRandom secureRandom = new SecureRandom();
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(secureRandom.nextInt("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".length()));
}
return new String(nonceChars);
}
}
......@@ -4,6 +4,13 @@ dts:
wx:
app-id: wx5b4409448bf2c72b
app-secret: 6fee0d7b2197845fed0a3fe8f5a9bd66
merchant-id: 123
private-key: 123
merchant-serial_number: 123
apiV3Key: 123123
pay-notify-url: 123
mch-id: 1538666666
mch-key: 1538xxxxxx-1538xxxxxx-1538xxxxxx
notify-url: https://test.dtsshop.com/demo/order/dtsNotify
......@@ -23,6 +30,17 @@ dts:
get-officialaccount-accesstoken-url: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s
officialaccount-templetId: WJkhzyY0_7AYqh19_ujK9yZhj4HohF8ETJvG5NYA9A8
#微信支付配置
wx-pay:
appId: wx5b4409448bf2c72b
merchantId: 123
privatekey: 123
merchantSerialNumber: 123
apiV3Key: 123123
payNotifyUrl: 123
privateKeyPath: 123
#腾讯IM配置
im:
SDKAppID: 1400489950
......
......@@ -44,6 +44,11 @@ public class DistributionOrderRequestDto extends BaseRequestDto implements Entit
*/
private Long buyerId;
/**
* 买方的微信openid
*/
private String buyerOpenid;
/**
* 店铺id
*/
......@@ -69,6 +74,11 @@ public class DistributionOrderRequestDto extends BaseRequestDto implements Entit
*/
private String logisticsCode;
/**
* 订单金额
*/
private String amount;
/**
* 运单号
*/
......@@ -83,4 +93,14 @@ public class DistributionOrderRequestDto extends BaseRequestDto implements Entit
* 分销订单状态
*/
private Integer state;
/**
* 微信支付订单号
*/
private String paymentId;
/**
* 预支付单号
*/
private String prepayId;
}
package com.wwdz.ch.wx.api;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
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.wwdz.ch.core.util.RedisUtils;
import com.xxdxxs.utils.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
/**
* 微信支付回调通知
*/
@RestController
@RequestMapping("/wxPayCallback")
public class WxPayCallbackController {
private static final Logger logger = LoggerFactory.getLogger(WxPayCallbackController.class);
@Autowired
RedisUtils redisUtils;
@Autowired
RSAAutoCertificateConfig rsaAutoCertificateConfig;
@RequestMapping(value ="/getPayNotify")
public synchronized String payNotify(HttpServletRequest request) throws IOException {
logger.info("------------------ 收到微信支付回调通知 ------------------");
// 请求头Wechatpay-Signature
String signature = request.getHeader("Wechatpay-Signature");
// 请求头Wechatpay-nonce
String nonce = request.getHeader("Wechatpay-Nonce");
// 请求头Wechatpay-Timestamp
String timestamp = request.getHeader("Wechatpay-Timestamp");
// 微信支付证书序列号
String serial = request.getHeader("Wechatpay-Serial");
// 签名方式
String signType = request.getHeader("Wechatpay-Signature-Type");
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serial)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.signType(signType)
.body(getRequestBody(request))
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
// 以支付通知回调为例,验签、解密并转换成 Transaction
logger.info("验签参数:{}", requestParam);
Map<String, String> returnMap = new HashMap<>();
returnMap.put("code", "FAIL");
returnMap.put("message", "失败");
Transaction transaction = null;
try {
transaction = parser.parse(requestParam, Transaction.class);
logger.info("验签成功!-支付回调结果:{}", transaction.toString());
if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {
logger.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", transaction.getOutTradeNo(), transaction.getTransactionId());
return JsonUtils.from(returnMap);
}
} catch (Exception e) {
logger.error("验签失败 : {}", e);
return JsonUtils.from(returnMap);
}
//商户订单号,分销订单号
String distributionOrderId = transaction.getOutTradeNo();
try {
//修改订单前,建议主动请求微信查询订单是否支付成功,防止恶意post
if (redisUtils.hasKey(distributionOrderId)) {
logger.info(">>>>>>>>>>> 分销单号【{}】, 正在被其它服务处理,当前不能操作 <<<<<<<<<<<<", distributionOrderId);
return JsonUtils.from(returnMap);
}
redisUtils.set(distributionOrderId, distributionOrderId);
//调用微信查询订单接口查询订单当前状态
//查询订单在系统中的当前状态
/*****/
//修改订单信息
/*****/
returnMap.put("code", "SUCCESS");
returnMap.put("message", "成功");
return JsonUtils.from(returnMap);
} catch (Exception e) {
logger.error("支付回调处理订单失败 error : {}", e);
} finally {
redisUtils.delete(distributionOrderId);
}
return JsonUtils.from(returnMap);
}
/**
* 获取请求体
*
* @param request
* @return
* @throws IOException
*/
public static String getRequestBody(HttpServletRequest request) throws IOException {
ServletInputStream stream = null;
BufferedReader reader = null;
StringBuffer sb = new StringBuffer();
try {
stream = request.getInputStream();
// 获取响应
reader = new BufferedReader(new InputStreamReader(stream));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
throw new IOException("读取返回支付接口数据流出现异常!");
} finally {
if (reader != null) {
reader.close();
}
if (stream != null) {
stream.close();
}
}
return sb.toString();
}
}
package com.wwdz.ch.wx.web.distribution;
import com.alibaba.fastjson.JSON;
import com.wwdz.ch.core.api.wxpay.WxPayServiceApi;
import com.wwdz.ch.core.type.Result;
import com.wwdz.ch.db.dto.request.distribution.DistributionOrderRequestDto;
import com.wwdz.ch.wx.service.distribution.DistributionOrderService;
......@@ -27,6 +28,9 @@ public class DistributionOrderController {
@Autowired
DistributionOrderService distributionOrderService;
@Autowired
WxPayServiceApi wxPayServiceApi;
@ApiOperation(value = "分销单下单,尚未付款")
@PostMapping("/create")
public Result createDistributionOrder(@RequestBody DistributionOrderRequestDto dto) {
......@@ -44,6 +48,20 @@ public class DistributionOrderController {
}
@ApiOperation(value = "前端获取支付所需参数")
@PostMapping("/getPayParam")
public Result getPayParam(@RequestBody DistributionOrderRequestDto dto) {
logger.info("前端获取支付所需参数,请求参数:{}", JSON.toJSONString(dto));
Validator validator = new Validator(FormHandler.ofEntity(dto));
validator.set("prepayId", "预支付单号").must().string()
.end();
if (!validator.isValid()) {
return Result.failed(validator.getErrorInfo());
}
return wxPayServiceApi.wxAppPayTuneUp(dto.getPrepayId());
}
@ApiOperation(value = "用户取消订单")
@PostMapping("/cancel")
public Result cancel(@RequestBody DistributionOrderRequestDto dto) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment