Explorar el Código

fix cryptomus支付

zwhui hace 3 años
padre
commit
5435670c1f

+ 2 - 1
netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java

@@ -262,7 +262,8 @@ public class OrderDon extends BaseEntity implements Serializable {
         PAY_EQUIPMENT("pay_equipment", "自购设备"),
         BALANCE("balance", "余额支付"),
         PAYPAL("paypal","paypal支付"),
-        STRIPE("stripe","stripe支付")
+        STRIPE("stripe","stripe支付"),
+        CRYPTOMUS("cryptomus","cryptomus支付")
         ;
         String type;
         String desc;

+ 34 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CryptomusPayReq.java

@@ -0,0 +1,34 @@
+package com.cyksj.model.request;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @author zwhui
+ * @date 2023/8/29 15:04
+ */
+@Data
+public class CryptomusPayReq {
+    private String amount;
+
+    private String currency;
+
+
+    private String order_id;
+
+    /**
+     * 成功付款后,用户可以点击付款表单上的按钮并返回到此URL。
+     */
+    private String url_success;
+
+    /**
+     * 付款状态的 网络钩子 将发送到的 URL
+     */
+    private String url_callback;
+
+    private String network;
+
+    private String status;
+
+}

+ 32 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CryptomusRefundReq.java

@@ -0,0 +1,32 @@
+package com.cyksj.model.request;
+
+import lombok.Data;
+
+/**
+ * @author zwhui
+ * @date 2023/8/29 16:08
+ */
+@Data
+public class CryptomusRefundReq {
+    /**
+     * 退款的地址
+     */
+    private String address;
+
+    /**
+     *
+     是从商家的余额中收取佣金还是从退款金额中收取佣金
+
+     true-从商家余额中获取佣金
+
+     false-将可退款金额减少佣金金额
+     */
+    private Boolean is_subtract;
+
+    /**
+     * 发票UUID
+     */
+    private String uuid;
+
+    private String order_id;
+}

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/request/OrderRefundReq.java

@@ -34,4 +34,9 @@ public class OrderRefundReq {
 	 * 订单金额、余额
 	 */
 	private String refundType;
+
+	/**
+	 * 加密货币退款地址
+	 */
+	private String address;
 }

+ 69 - 0
netflix-dao/src/main/java/com/cyksj/model/response/CryptomusPayRep.java

@@ -0,0 +1,69 @@
+package com.cyksj.model.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author zwhui
+ * @date 2023/8/29 15:18
+ */
+@NoArgsConstructor
+@Data
+public class CryptomusPayRep {
+
+
+    @JsonProperty("state")
+    private Integer state;
+    @JsonProperty("result")
+    private Result result;
+
+    @NoArgsConstructor
+    @Data
+    public static class Result {
+        @JsonProperty("uuid")
+        private String uuid;
+        @JsonProperty("order_id")
+        private String orderId;
+        @JsonProperty("amount")
+        private String amount;
+        @JsonProperty("payment_amount")
+        private Object paymentAmount;
+        @JsonProperty("payer_amount")
+        private Object payerAmount;
+        @JsonProperty("discount_percent")
+        private Object discountPercent;
+        @JsonProperty("discount")
+        private String discount;
+        @JsonProperty("payer_currency")
+        private Object payerCurrency;
+        @JsonProperty("currency")
+        private String currency;
+        @JsonProperty("merchant_amount")
+        private Object merchantAmount;
+        @JsonProperty("network")
+        private Object network;
+        @JsonProperty("address")
+        private Object address;
+        @JsonProperty("from")
+        private Object from;
+        @JsonProperty("txid")
+        private Object txid;
+        @JsonProperty("payment_status")
+        private String paymentStatus;
+        @JsonProperty("url")
+        private String url;
+        @JsonProperty("expired_at")
+        private Integer expiredAt;
+        @JsonProperty("status")
+        private String status;
+        @JsonProperty("is_final")
+        private Boolean isFinal;
+        @JsonProperty("additional_data")
+        private Object additionalData;
+        @JsonProperty("created_at")
+        private String createdAt;
+        @JsonProperty("updated_at")
+        private String updatedAt;
+    }
+}

+ 34 - 0
netflix-dao/src/main/java/com/cyksj/model/response/CryptomusRefundRep.java

@@ -0,0 +1,34 @@
+package com.cyksj.model.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @author zwhui
+ * @date 2023/8/29 16:06
+ */
+@NoArgsConstructor
+@Data
+public class CryptomusRefundRep {
+
+    @JsonProperty("state")
+    private Integer state;
+    @JsonProperty("errors")
+    private Errors errors;
+    @JsonProperty("message")
+    private String message;
+    @JsonProperty("result")
+    private List<?> result;
+
+    @NoArgsConstructor
+    @Data
+    public static class Errors {
+        @JsonProperty("uuid")
+        private List<String> uuid;
+        @JsonProperty("order_id")
+        private List<String> orderId;
+    }
+}

+ 52 - 0
netflix-dao/src/main/java/com/cyksj/model/response/CryptomusReturnRep.java

@@ -0,0 +1,52 @@
+package com.cyksj.model.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author zwhui
+ * @date 2023/8/29 15:02
+ */
+@NoArgsConstructor
+@Data
+public class CryptomusReturnRep {
+
+    @JsonProperty("type")
+    private String type;
+    @JsonProperty("uuid")
+    private String uuid;
+    @JsonProperty("order_id")
+    private String orderId;
+    @JsonProperty("amount")
+    private String amount;
+    @JsonProperty("payment_amount")
+    private String paymentAmount;
+    @JsonProperty("payment_amount_usd")
+    private String paymentAmountUsd;
+    @JsonProperty("merchant_amount")
+    private String merchantAmount;
+    @JsonProperty("commission")
+    private String commission;
+    @JsonProperty("is_final")
+    private Boolean isFinal;
+    @JsonProperty("status")
+    private String status;
+    @JsonProperty("from")
+    private String from;
+    @JsonProperty("wallet_address_uuid")
+    private Object walletAddressUuid;
+    @JsonProperty("network")
+    private String network;
+    @JsonProperty("currency")
+    private String currency;
+    @JsonProperty("payer_currency")
+    private String payerCurrency;
+    @JsonProperty("additional_data")
+    private Object additionalData;
+    @JsonProperty("txid")
+    private String txid;
+    @JsonProperty("sign")
+    private String sign;
+
+}

+ 143 - 0
netflix-service/src/main/java/com/cyksj/service/cryptomus/CryptomusService.java

@@ -0,0 +1,143 @@
+package com.cyksj.service.cryptomus;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.crypto.digest.MD5;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.Codec;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.enums.GatewayApiCode;
+import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.request.CryptomusPayReq;
+import com.cyksj.model.request.CryptomusRefundReq;
+import com.cyksj.model.request.OrderRefundReq;
+import com.cyksj.model.response.CryptomusPayRep;
+import com.cyksj.model.response.CryptomusRefundRep;
+import com.cyksj.model.response.CryptomusReturnRep;
+import com.cyksj.service.order.OrderDonService;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.bouncycastle.crypto.digests.MD5Digest;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+
+/**
+ * @author zwhui
+ * @date 2023/8/29 15:20
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class CryptomusService {
+
+    private final OrderDonService orderDonService;
+
+
+
+    @Value("${cryptomus.shopId}")
+    private String shopId;
+
+    @Value("${cryptomus.shopKey}")
+    private String shopKey;
+
+    @Value("${cryptomus.host}")
+    private String host;
+
+
+    public String pay(Long orderId, String successUrl) throws Exception {
+        OrderDon orderDon = orderDonService.getById(orderId);
+        orderDon.setType(OrderDon.Type.CRYPTOMUS);
+        orderDonService.updateById(orderDon);
+        if (OrderDon.Status.hasPayment.equals(orderDon.getStatus())) {
+            throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"The donation has been paid");
+        }
+        CryptomusPayReq cryptomusPay = new CryptomusPayReq();
+        cryptomusPay.setAmount(String.valueOf(orderDon.getMoney().divide(new BigDecimal(100), 2, RoundingMode.HALF_DOWN)));
+        cryptomusPay.setCurrency("USD");
+        cryptomusPay.setOrder_id(orderId.toString());
+        cryptomusPay.setUrl_callback(host + "/applets/order/cryptomus/notify");
+        cryptomusPay.setUrl_success(successUrl);
+        String json = Jsons.toJson(cryptomusPay);
+        String result = HttpRequest.post("https://api.cryptomus.com/v1/payment").header("merchant", shopId).header("sign", MD5.create().digestHex(Base64.encode(json) + shopKey)).body(json).execute().body();
+
+        return Jsons.parseObject(result, CryptomusPayRep.class).getResult().getUrl();
+    }
+
+    public void testPay(Long orderId, String successUrl) throws Exception {
+        OrderDon orderDon = orderDonService.getById(orderId);
+        orderDon.setType(OrderDon.Type.CRYPTOMUS);
+        orderDonService.updateById(orderDon);
+        if (OrderDon.Status.hasPayment.equals(orderDon.getStatus())) {
+            throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"The donation has been paid");
+        }
+        CryptomusPayReq cryptomusPay = new CryptomusPayReq();
+        cryptomusPay.setAmount(String.valueOf(orderDon.getMoney().divide(new BigDecimal(100), 2, RoundingMode.HALF_DOWN)));
+        cryptomusPay.setCurrency("ETH");
+        cryptomusPay.setNetwork("eth");
+        cryptomusPay.setStatus("paid");
+        cryptomusPay.setOrder_id(orderId.toString());
+        cryptomusPay.setUrl_callback(host + "/applets/order/cryptomus/notify");
+        String json = Jsons.toJson(cryptomusPay);
+        String result = HttpRequest.post("https://api.cryptomus.com/v1/test-webhook/payment").header("merchant", shopId).header("sign", MD5.create().digestHex(Base64.encode(json) + shopKey)).body(json).execute().body();
+        System.out.println(result);
+    }
+
+    public void notify(CryptomusReturnRep cryptomusReturnRep) throws Exception {
+        log.info("cryptomus 支付回调 用户信息:{}", cryptomusReturnRep);
+        //支付成功
+        if("paid".equals(cryptomusReturnRep.getStatus())){
+            String sign = cryptomusReturnRep.getSign();
+            ObjectMapper mapper = new ObjectMapper();
+            ObjectNode jsonNode = mapper.valueToTree(cryptomusReturnRep);
+            jsonNode.remove("sign");
+            String json = mapper.writeValueAsString(jsonNode);
+            String hash = MD5.create().digestHex(Base64.encode(json) + shopKey);
+            if (!sign.equals(hash)){
+                throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"System error");
+            }
+            String orderId = cryptomusReturnRep.getOrderId();
+            String transationId = cryptomusReturnRep.getUuid();
+            String commission = cryptomusReturnRep.getCommission();
+            //更新订单支付状态
+            OrderDon orderDon = orderDonService.getById(orderId);
+            orderDon.setTransactionId(transationId);
+            orderDon.setPayTime(new Date());
+            orderDon.setAbroadTransactionFee(new BigDecimal(commission).multiply(BigDecimal.valueOf(100)));
+            orderDonService.orderNotify(orderDon);
+            log.info("支付成功订单:"+orderId+",支付交易号:"+transationId);
+            log.info("支付成功订单:"+orderId);
+        }
+    }
+
+
+    public Boolean refund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception {
+        BigDecimal net = refundReq.getRefundMoney().subtract(orderDon.getAbroadTransactionFee());
+        log.info("cryptomus return order:{},money:{}",orderDon.getId(),net);
+        CryptomusRefundReq cryptomusRefundReq = new CryptomusRefundReq();
+        cryptomusRefundReq.setAddress(refundReq.getAddress());
+        cryptomusRefundReq.setUuid(orderDon.getTransactionId());
+        cryptomusRefundReq.setOrder_id(orderDon.getId().toString());
+        cryptomusRefundReq.setIs_subtract(false);
+        String json = Jsons.toJson(cryptomusRefundReq);
+        String result = HttpRequest.post("https://api.cryptomus.com/v1/payment/refund").header("merchant", shopId).header("sign", MD5.create().digestHex(Base64.encode(json) + shopKey)).body(json).execute().body();
+        CryptomusRefundRep cryptomusRefundRep = Jsons.parseObject(result, CryptomusRefundRep.class);
+        if (cryptomusRefundRep.getState() == 0){
+            orderDon.setStatus(OrderDon.Status.refund);
+            orderDonService.updateById(orderDon);
+            return true;
+        }
+        log.info("paypal return refund:{}",cryptomusRefundRep);
+        return false;
+    }
+
+
+}

+ 9 - 0
netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java

@@ -40,6 +40,7 @@ import com.cyksj.model.request.ClickOutReq;
 import com.cyksj.model.request.OrderRefundReq;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.coupon.CouponFontService;
+import com.cyksj.service.cryptomus.CryptomusService;
 import com.cyksj.service.distribute.equipment.EquipmentDistributeService;
 import com.cyksj.service.mange.CmsOrderDonService;
 import com.cyksj.service.mange.stock.GoodsDonStockService;
@@ -135,6 +136,8 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
 
     private final StripeService stripeService;
 
+    private final CryptomusService cryptomusService;
+
     private final OrderDiscountPlusPurchaseSkuRelationMapper orderDiscountPlusPurchaseSkuRelationMapper;
 
     private final EquipmentUserDistributeTransportBenefitsMapper transportBenefitsMapper;
@@ -297,6 +300,9 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
         if (type == OrderDon.Type.STRIPE && !refundType.equals(OrderDon.Type.BALANCE.getType())) {
             stripeService.refund(refundReq, orderDon);
         }
+        if (type == OrderDon.Type.CRYPTOMUS && !refundType.equals(OrderDon.Type.BALANCE.getType())) {
+            cryptomusService.refund(refundReq, orderDon);
+        }
         orderDon.setRefundDesc(refundReq.getRefundDesc());
         //记录退款信息
         commonRefund(orderDon, goodsDon, sku, outNo, refundType);
@@ -554,6 +560,9 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
         if (OrderDon.Status.noPayment == orderDon.getStatus() || OrderDon.Status.close == orderDon.getStatus()) {
             throw BusinessRuntimeException.getInstance("订单{0}状态不支持退款", orderDon.getStatus());
         }
+        if (OrderDon.Type.CRYPTOMUS.getType().equals(refundType) && StringUtil.isEmpty(refundReq.getAddress())) {
+            throw BusinessRuntimeException.getInstance("请填写退款地址");
+        }
         if (goodsDon.getType() == 1) {
             //虚拟原订单是否已过期
             Integer months = sku.getMonths();

+ 41 - 0
netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java

@@ -28,8 +28,10 @@ import com.cyksj.model.manage.views.OrderDonView;
 import com.cyksj.model.request.AlipayOrderDonReq;
 import com.cyksj.model.request.OrderPayRequest;
 import com.cyksj.model.response.AliPayTradeStatus;
+import com.cyksj.model.response.CryptomusReturnRep;
 import com.cyksj.model.views.GoodsDonSkuView;
 import com.cyksj.model.views.OrderCommentView;
+import com.cyksj.service.cryptomus.CryptomusService;
 import com.cyksj.service.order.OrderDonService;
 import com.cyksj.service.paypal.PayPalService;
 import com.cyksj.service.paypal.PaypalPayConfigService;
@@ -120,6 +122,9 @@ public class OrderController {
     @Autowired
     private UserBindRelationService userBindRelationService;
 
+    @Autowired
+    private CryptomusService cryptomusService;
+
     /**
      * 支付
      * @author chan
@@ -606,4 +611,40 @@ public class OrderController {
 
 
 
+    /**
+     * cryptomus支付
+     */
+    @PostMapping("/cryptomus/pay/{orderId}")
+    public Result<String> cryptomusPay(@PathVariable Long orderId,String successUrl) throws Exception {
+        final String methodName = "cryptomus支付";
+        log.info("{}[start] orderId:{}",methodName,orderId);
+
+        String url = cryptomusService.pay(orderId, successUrl);
+        log.info("cryptomus支付成功[end] url:{}",url);
+
+        return GatewayResponse.SUCCESS.newBuilder().toResult(url);
+    }
+
+    @PostMapping("/test/cryptomus/pay/{orderId}")
+    public Result<String> testCryptomusPay(@PathVariable Long orderId,String successUrl) throws Exception {
+        final String methodName = "cryptomus支付";
+        log.info("{}[start] orderId:{}",methodName,orderId);
+
+        cryptomusService.testPay(orderId, successUrl);
+        log.info("cryptomus支付成功[end]");
+
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
+
+    /**
+     * cryptomus支付回调
+     */
+    @RequestMapping(value = "/cryptomus/notify")
+    public void cryptomusNotify(@RequestBody CryptomusReturnRep cryptomusReturnRep) throws Exception {
+        cryptomusService.notify(cryptomusReturnRep);
+    }
+
+
+
 }

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
netflix-web/src/main/resources/application-dev.yml


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
netflix-web/src/main/resources/application-prd.yml


+ 6 - 1
netflix-web/src/main/resources/application-pre.yml

@@ -172,4 +172,9 @@ advertisementwxapp:
 
 google:
   clientId: 371004574299-hhj668e7g9mbvh3o4dentfniinmu6kfk.apps.googleusercontent.com
-  clientSecret: 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
+  clientSecret: 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
+
+cryptomus:
+  shopId: 15d39001-dec3-4949-8872-69b241825c5d
+  shopKey: zk4alqQNueK2RiWH2YM3auEEDbCxYZGh2XsJQv3AdoJYASQkUxvaGR0ID81xIg1sm0H3ZMeWgDzIFlqMYJeqrBvQeFRHceEH83PABCiAYBeGp75XfqoUN8Oa1No1UYas
+  host: https://nf.video/8081/api/

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio