记得上下班打卡 | git大法好,push需谨慎

Commit 926390cc authored by wangyifan's avatar wangyifan

收钱吧最新接口对接

parent a75fcf55
package com.liquidnet.service.goblin.param.shouqianba.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "创建收单请求")
public class AcquiringCreateRequest {
@ApiModelProperty(required = true, value = "应用id", example = "202507xxxxxxx")
private String appid;
@ApiModelProperty(required = true, value = "卖家信息")
private CommonRequest.Seller seller;
@ApiModelProperty(required = true, value = "卖家信息")
private Order orderID;
@Data
@ApiModel(value = "订单信息")
public static class Order {
@ApiModelProperty(required = true, value = "订单ID")
private String sn;
@ApiModelProperty(required = true, value = "订单密码")
private String signature;
}
}
......@@ -12,6 +12,9 @@ public class CashierQueryRequest {
@ApiModelProperty(required = true, value = "应用id", example = "202507xxxxxxx")
private String appid;
@ApiModelProperty(required = true, value = "商户信息")
private CommonRequest.Seller seller;
@ApiModelProperty(required = true, value = "支付模式代码", example = "4")
private Integer paymentMode;
......@@ -19,41 +22,25 @@ public class CashierQueryRequest {
private PaymentEnv paymentEnv;
@ApiModelProperty(required = false, value = "付款人信息")
private Payer payer;
private CommonRequest.Payer payer;
@ApiModelProperty(required = true, value = "收单信息")
private AcquiringInfo acquiringInfo;
private CommonRequest.Acquiring acquiringInfo;
@Data
@ApiModel(value = "支付环境信息")
public static class PaymentEnv {
@ApiModelProperty(required = false, value = "ip地址")
@ApiModelProperty(required = false, value = "ip地址 最大40位")
private String ip;
@ApiModelProperty(required = true, value = "支付客户端", example = "wechat")
@ApiModelProperty(required = true, value = "支付客户端 最大32位", example = "wechat")
private String client;
@ApiModelProperty(required = false, value = "设备号")
@ApiModelProperty(required = false, value = "设备号 最大64位")
private String device;
@ApiModelProperty(required = false, value = "版本号", example = "1.4")
private String version;
}
@Data
@ApiModel(value = "付款人信息")
public static class Payer {
@ApiModelProperty(required = true, value = "付款人Id")
private String userId;
}
@Data
@ApiModel(value = "收单信息")
public static class AcquiringInfo {
@ApiModelProperty(required = true, value = "收单号")
private String acquiringSn;
@ApiModelProperty(required = true, value = "收单签名")
private String signature;
}
}
package com.liquidnet.service.goblin.param.shouqianba.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class CommonRequest {
@Data
@ApiModel(value = "商城信息")
public static class Mall{
@ApiModelProperty(required = true, value = "商城ID")
private String mallSn;
@ApiModelProperty(required = true, value = "商城密码")
private String signature;
}
@Data
@ApiModel(value = "卖家信息/商户信息")
public static class Seller {
@ApiModelProperty(required = true, value = "商户ID")
private String merchantId;
@ApiModelProperty(required = true, value = "商户userID")
private String merchantUserId;
@ApiModelProperty(required = true, value = "角色 固定值(super_admin)")
private String role;
}
@Data
@ApiModel("买家信息")
public static class Buyer{
@ApiModelProperty(required = true, value = "买家ID(用户唯一标识)")
private String buyerId;
}
@Data
@ApiModel("付款人信息")
public static class Payer{
@ApiModelProperty(required = true, value = "付款人ID")
private String userId;
}
@ApiModel(value = "收单信息")
@Data
public static class Acquiring{
@ApiModelProperty(value = "收单号")
private String acquiringSn;
@ApiModelProperty(value = "收单密钥")
private String signature;
}
}
......@@ -15,7 +15,7 @@ public class CouponQueryRequest {
private OrderInfo orderID;
@ApiModelProperty(required = true, value = "卖家信息", example = "4")
private Seller seller;
private CommonRequest.Seller seller;
@Data
@ApiModel(value = "订单信息")
......@@ -28,16 +28,5 @@ public class CouponQueryRequest {
}
@Data
@ApiModel(value = "卖家信息")
public static class Seller {
@ApiModelProperty(required = true, value = "商户ID")
private String merchantId;
@ApiModelProperty(required = true, value = "商户userID")
private String merchantUserId;
@ApiModelProperty(required = true, value = "角色")
private String role;
}
}
......@@ -15,7 +15,7 @@ public class CouponRefundRequest {
private String appid;
@ApiModelProperty(required = true, value = "卖家信息")
private SellerInfo seller;
private CommonRequest.Seller seller;
@ApiModelProperty(required = true, value = "订单信息")
private OrderInfo orderID;
......@@ -29,23 +29,6 @@ public class CouponRefundRequest {
@ApiModelProperty(required = true, value = "退款来源(固定值:EXTERN)")
private String requestSource;
@ApiModelProperty(required = false, value = "拓展参数(自定义信息)")
private Map<String, Object> bizExtended;
@Data
@ApiModel(value = "卖家信息")
public static class SellerInfo {
@ApiModelProperty(required = true, value = "商户Id(固定值)")
private String merchantId;
@ApiModelProperty(required = true, value = "商户userId(固定值)")
private String merchantUserId;
@ApiModelProperty(required = true, value = "角色(固定值:super_admin)")
private String role;
}
@Data
@ApiModel(value = "订单信息")
public static class OrderInfo {
......
......@@ -14,11 +14,14 @@ public class CreateWechatPrepayOrderRequest {
@ApiModelProperty(required = true, value = "应用id", example = "202507xxxxxxx")
private String appid;
@ApiModelProperty(required = true, value = "商户信息")
private CommonRequest.Seller seller;
@ApiModelProperty(required = true, value = "收单号")
private String acquiringSn;
@ApiModelProperty(required = true, value = "收单签名")
private Object signature;
private String signature;
@ApiModelProperty(required = true, value = "支付工具")
private List<UsingPayTool> usingPayTools;
......
......@@ -24,42 +24,21 @@ public class OrderCreateRequest implements Serializable {
private String appid;
@ApiModelProperty(required = true, value = "商城信息")
private MallInfo mallID;
private CommonRequest.Mall mallID;
@ApiModelProperty(required = true, value = "商户信息")
private CommonRequest.Seller seller;
@ApiModelProperty(required = true, value = "结算项ID")
private String checkoutItemsId;
@ApiModelProperty(required = true, value = "买家信息")
private Buyer buyer;
private CommonRequest.Buyer buyer;
@ApiModelProperty(required = true, value = "请求ID")
@ApiModelProperty(required = true, value = "请求ID 不超过40位")
private String requestId;
@ApiModelProperty(required = true, value = "场景信息")
private List<Scene> scenes;
@ApiModelProperty(required = true, value = "自定义场景信息")
private List<Scene> extBizScenes;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(value = "商城信息")
public static class MallInfo extends SettlementCreateRequest.MallInfo{
}
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("买家信息")
public static class Buyer extends SettlementCreateRequest.Buyer{
}
@ApiModel(value = "场景信息")
public static class Scene{
@ApiModelProperty(value = "模块编号")
private String moduleSn;
@ApiModelProperty(required = true, value = "标题 不超过32")
private String subject;
@ApiModelProperty(value = "场景数据")
private String content;
}
}
......@@ -23,10 +23,13 @@ public class SettlementCreateRequest implements Serializable {
private String appid;
@ApiModelProperty(required = true, value = "商城信息")
private MallInfo mallID;
private CommonRequest.Mall mallID;
@ApiModelProperty(required = true, value = "商户信息")
private CommonRequest.Seller seller;
@ApiModelProperty(required = true, value = "买家信息")
private Buyer buyer;
private CommonRequest.Buyer buyer;
@ApiModelProperty(required = false, value = "结算明细组(结算条目列表)")
private List<CheckoutItem> checkoutItems;
......@@ -35,27 +38,6 @@ public class SettlementCreateRequest implements Serializable {
private Long amount;
@Data
@ApiModel(value = "商城信息")
public static class MallInfo{
@ApiModelProperty(required = true, value = "商城ID")
private String mallSn;
@ApiModelProperty(required = true, value = "商城密码")
private String signature;
}
@Data
@ApiModel("买家信息")
public static class Buyer{
@ApiModelProperty(required = true, value = "买家ID(用户唯一标识)")
private String buyerId;
}
@Data
@ApiModel(value = "结算明细条目")
public static class CheckoutItem{
......@@ -73,7 +55,7 @@ public class SettlementCreateRequest implements Serializable {
private String quantity;
@ApiModelProperty(required = true, value = "商品类型(0 正常商品 1 自定义商品)")
private String type;
private Byte type;
@ApiModelProperty(required = true, value = "商品标题")
private String title;
......
package com.liquidnet.service.goblin.param.shouqianba.response;
import com.liquidnet.service.goblin.param.shouqianba.response.data.AcquiringCreateData;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "创建收单响应参数")
@Data
public class AcquiringCreateResponse extends BaseResponse<AcquiringCreateData> {
}
package com.liquidnet.service.goblin.param.shouqianba.response.data;
import com.liquidnet.service.goblin.param.shouqianba.request.CommonRequest;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(value = "创建收单Data")
public class AcquiringCreateData extends CommonRequest.Acquiring {
}
package com.liquidnet.service.goblin.param.shouqianba.response.data;
import com.liquidnet.service.goblin.param.shouqianba.request.CommonRequest;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
......@@ -16,16 +17,8 @@ public class OrderCreateData {
private String orderSignature;
@ApiModelProperty(value = "收单信息)")
private Acquiring acquiring;
private CommonRequest.Acquiring acquiring;
@ApiModel(value = "收单信息")
@Data
public static class Acquiring{
@ApiModelProperty(value = "收单号)")
private String acquiringSn;
@ApiModelProperty(value = "收单号)")
private String signature;
}
}
package com.liquidnet.service.goblin.service;
import com.liquidnet.service.goblin.param.shouqianba.request.CashierQueryRequest;
import com.liquidnet.service.goblin.param.shouqianba.request.OrderCreateRequest;
import com.liquidnet.service.goblin.param.shouqianba.request.SettlementCreateRequest;
import com.liquidnet.service.goblin.param.shouqianba.response.data.CashierQueryData;
import com.liquidnet.service.goblin.param.shouqianba.response.data.OrderCreateData;
import com.liquidnet.service.goblin.param.shouqianba.response.data.SettlementCreateData;
import com.liquidnet.service.goblin.param.shouqianba.request.*;
import com.liquidnet.service.goblin.param.shouqianba.response.data.*;
/**
* 收钱吧对接接口定义
......@@ -33,10 +29,45 @@ public interface IGoblinShouQianBaService {
*/
OrderCreateData createOrder(OrderCreateRequest request);
/**
* 创建收单
* 如果上一步创建收单失败 可以使用这个接口重新创建收单
* @return
*/
AcquiringCreateData createAcquiring(AcquiringCreateRequest request);
/**
* 查询收银台
* @param request
* @return
*/
CashierQueryData queryCashier(CashierQueryRequest request);
/**
* 创建微信预支付订单
* @param request
* @return
*/
CreateWechatPrepayOrderData createWechatPrepayOrder(CreateWechatPrepayOrderRequest request);
/**
* 查询券码
* @param request
* @return
*/
CouponQueryData queryCoupon(CouponQueryRequest request);
/**
* 券码状态同步
* @param request
* @return
*/
boolean syncCouponStatus(CouponStatusSyncRequest request);
/**
* 券退款
* @param request
* @return
*/
CouponRefundData refundCoupon(CouponRefundRequest request);
}
......@@ -4,20 +4,22 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.liquidnet.commons.lang.util.HttpUtil;
import com.liquidnet.commons.lang.util.MD5Utils;
import com.liquidnet.service.goblin.param.shouqianba.request.CashierQueryRequest;
import com.liquidnet.service.goblin.param.shouqianba.request.OrderCreateRequest;
import com.liquidnet.service.goblin.param.shouqianba.request.SettlementCreateRequest;
import com.liquidnet.service.goblin.param.shouqianba.request.*;
import com.liquidnet.service.goblin.param.shouqianba.response.OrderCreateResponse;
import com.liquidnet.service.goblin.param.shouqianba.response.SettlementCreateResponse;
import com.liquidnet.service.goblin.param.shouqianba.response.data.CashierQueryData;
import com.liquidnet.service.goblin.param.shouqianba.response.data.OrderCreateData;
import com.liquidnet.service.goblin.param.shouqianba.response.data.SettlementCreateData;
import com.liquidnet.service.goblin.param.shouqianba.response.data.*;
import com.liquidnet.service.goblin.service.IGoblinShouQianBaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.TreeMap;
/**
......@@ -36,6 +38,8 @@ public class GoblinShouQianBaServiceImpl implements IGoblinShouQianBaService {
public static final String BASE_API = "https://api.shouqianba.com";
private static final String APPID = "test_appid";
private static final String APPKEY = "test_appkey";
// TODO: 收钱吧公钥,建议改为配置
private static final String SHOUQIANBA_PUB_KEY = "test_pub_key";
/**
* 创建结算明细
......@@ -82,7 +86,7 @@ public class GoblinShouQianBaServiceImpl implements IGoblinShouQianBaService {
@Override
public OrderCreateData createOrder(OrderCreateRequest request) {
final String url = BASE_API + "/optimus/core/mall/createPreOrder";
final String url = BASE_API + "/optimus/core/order/placeOrder";
log.info("[收钱吧] 创建订单, 请求URL: {}, 请求参数: {}", url, request);
try {
......@@ -117,12 +121,37 @@ public class GoblinShouQianBaServiceImpl implements IGoblinShouQianBaService {
}
}
@Override
public AcquiringCreateData createAcquiring(AcquiringCreateRequest request) {
return null;
}
@Override
public CashierQueryData queryCashier(CashierQueryRequest request) {
return null;
}
@Override
public CreateWechatPrepayOrderData createWechatPrepayOrder(CreateWechatPrepayOrderRequest request) {
return null;
}
@Override
public CouponQueryData queryCoupon(CouponQueryRequest request) {
return null;
}
@Override
public boolean syncCouponStatus(CouponStatusSyncRequest request) {
return false;
}
@Override
public CouponRefundData refundCoupon(CouponRefundRequest request) {
return null;
}
private String getSign(String bodyJsonStr) {
try {
// 签名算法: sign = MD5( CONCAT( body + appkey ) )
......@@ -136,4 +165,40 @@ public class GoblinShouQianBaServiceImpl implements IGoblinShouQianBaService {
return "";
}
}
/**
* 收钱吧回调推送验签
*
* @param signature 收钱吧推送来的签名 (signature)
* @param eventId 事件ID (eventId)
* @param timestamp 时间戳 (timestamp)
* @param nonce 随机数 (nonce)
* @param content 业务数据报文 (content)
* @return 验签是否通过
*/
public boolean verifySignature(String signature, String eventId, String timestamp, String nonce, String content) {
// 签名原串 = eventId + timestamp + nonce + content
String plaintext = eventId + timestamp + nonce + content;
return verifySignatureSHA256WithRSA(plaintext, signature, SHOUQIANBA_PUB_KEY);
}
public boolean verifySignatureSHA256WithRSA(String plaintext, String inputSignature, String pubKey) {
return verifySignatureSHA256WithRSA(plaintext.getBytes(StandardCharsets.UTF_8), inputSignature, pubKey);
}
public boolean verifySignatureSHA256WithRSA(byte[] plaintextByte, String inputSignature, String pubKey) {
try {
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(bobPubKeySpec);
byte[] signed = Base64.getDecoder().decode(inputSignature);
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(publicKey);
signature.update(plaintextByte);
return signature.verify(signed);
} catch (Exception e) {
log.error("[收钱吧] 验签异常, signature={}, pubKey={}", inputSignature, pubKey, e);
return false;
}
}
}
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