diff --git a/mallinkService/src/main/java/com/iformall/douyin/pay/DouYinPayHelper.java b/mallinkService/src/main/java/com/iformall/douyin/pay/DouYinPayHelper.java new file mode 100644 index 000000000..ffffc081a --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/douyin/pay/DouYinPayHelper.java @@ -0,0 +1,144 @@ +package com.iformall.douyin.pay; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.iformall.douyin.pay.orderQuery.OrderQueryResult; +import com.iformall.douyin.pay.preOrder.CreatePreOrderResult; +import com.iformall.douyin.pay.preOrder.DouYinCreatePreOrder; +import com.iformall.utils.HashUtil; +import com.iformall.utils.HttpUtil; + +import lombok.extern.slf4j.Slf4j; + +/** + * 抖音小程序支付 + * https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/api/open-interface/payment/secure/YE + * @author alascor + */ +@Slf4j +public class DouYinPayHelper { + + /** + * urlEncode + * + * @param src + * 微信参数 + * @return String + * @throws UnsupportedEncodingException + * 编码错误 + */ + private static String urlEncode(String src) throws UnsupportedEncodingException { + return URLEncoder.encode(src, Charset.forName("UTF-8").name()).replace("+", "%20"); + } + + /** + * 组装签名的字段 + * + * @param params + * 参数 + * @param urlEncoder + * 是否urlEncoder + * @return String + */ + private static String packageSign(Map params, boolean urlEncoder) { + // 先将参数以其参数名的字典序升序进行排序 + TreeMap sortedParams = new TreeMap(params); + // 遍历排序后的字典,字段内容(不包含 key)与支付 SALT 一起进行字典序排序后,使用&符号链接 + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Entry param : sortedParams.entrySet()) { + String value = param.getValue(); + if (StringUtils.isBlank(value)) { + continue; + } + if (first) { + first = false; + } else { + sb.append("&"); + } + if (urlEncoder) { + try { + value = urlEncode(value); + } catch (UnsupportedEncodingException e) { + } + } + sb.append(value); + } + return sb.toString(); + } + public static String createSign(Map params, String sercrect) { + // 生成签名前先去除sign + params.remove("sign"); + params.remove("app_id"); + params.remove("thirdparty_id"); + String stringA = packageSign(params, false); + String stringSignTemp = stringA + "&" + sercrect; + return HashUtil.md5(stringSignTemp).toUpperCase(); + } + + public static String doPost(String url, Map params) { + return HttpUtil.payPost(url, JSON.toJSONString(params)); + } + + //服务端预下单 + public static CreatePreOrderResult createPreOrder(DouYinCreatePreOrder preOrder) { + String response = doPost("https://developer.toutiao.com/api/apps/ecpay/v1/create_order", preOrder.toRequestMap()); + JSONObject jsonObject = JSON.parseObject(response); + Integer code = jsonObject.getInteger("err_no"); + if (null != code && code.intValue() == 0 ) { + CreatePreOrderResult result = new CreatePreOrderResult(); + result.setSuccess(true); + JSONObject data = jsonObject.getJSONObject("data"); + if (null != data ) { + result.setOrderId(data.getString("order_id")); + result.setToken(data.getString("order_token")); + }else { + log.error("createPreOrder reponse empity. request: "+JSON.toJSONString(preOrder.toRequestMap())+" response:"+response); + result.setSuccess(false); + result.setMsg("createPreOrder reponse empity."+response); + } + return result; + }else { + log.error("createPreOrder reponse error. request: "+JSON.toJSONString(preOrder.toRequestMap())+" response:"+response); + CreatePreOrderResult result = new CreatePreOrderResult(); + result.setSuccess(false); + result.setMsg("createPreOrder reponse error."+response); + return result; + } + } + + + //订单查询 + public static OrderQueryResult orderQuery(String appId,String secrect,String orderNo,String thirdParytyId) { + Map map = new HashMap(); + map.put("app_id",appId); + map.put("out_order_no",orderNo); + map.put("sign",createSign(map, secrect)); + String response = doPost("https://developer.toutiao.com/api/apps/ecpay/v1/query_order", map); + JSONObject jsonObject = JSON.parseObject(response); + JSONObject info = jsonObject.getJSONObject("payment_info"); + if (null != info) { + OrderQueryResult result = new OrderQueryResult(); + result.setChannelGatewayNo(info.getString("channel_gateway_no")); + result.setChannelNo(info.getString("channel_no")); + result.setWay(info.getInteger("way")); + result.setPayTime(info.getString("pay_time")); + result.setOrderStatus(info.getString("order_status")); + result.setTotalFee(info.getInteger("total_fee")); + return result; + }else { + log.error("orderQuery reponse error. request: "+JSON.toJSONString(map)+" response:"+response); + return null; + } + } +} diff --git a/mallinkService/src/main/java/com/iformall/douyin/pay/orderQuery/OrderQueryResult.java b/mallinkService/src/main/java/com/iformall/douyin/pay/orderQuery/OrderQueryResult.java new file mode 100644 index 000000000..d6e259f6d --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/douyin/pay/orderQuery/OrderQueryResult.java @@ -0,0 +1,14 @@ +package com.iformall.douyin.pay.orderQuery; + +import lombok.Data; + +@Data +public class OrderQueryResult { + + private Integer totalFee; + private String orderStatus;//"PROCESSING-处理中|SUCCESS-成功|FAIL-失败|TIMEOUT-超时" + private String payTime; + private Integer way; + private String channelNo; + private String channelGatewayNo; +} diff --git a/mallinkService/src/main/java/com/iformall/douyin/pay/preOrder/CreatePreOrderResult.java b/mallinkService/src/main/java/com/iformall/douyin/pay/preOrder/CreatePreOrderResult.java new file mode 100644 index 000000000..f2011d502 --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/douyin/pay/preOrder/CreatePreOrderResult.java @@ -0,0 +1,12 @@ +package com.iformall.douyin.pay.preOrder; + +import lombok.Data; + +@Data +public class CreatePreOrderResult { + + private boolean success; + private String msg; + private String orderId; + private String token; +} diff --git a/mallinkService/src/main/java/com/iformall/douyin/pay/preOrder/DouYinCreatePreOrder.java b/mallinkService/src/main/java/com/iformall/douyin/pay/preOrder/DouYinCreatePreOrder.java new file mode 100644 index 000000000..8b7cf153f --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/douyin/pay/preOrder/DouYinCreatePreOrder.java @@ -0,0 +1,45 @@ +package com.iformall.douyin.pay.preOrder; + +import java.util.HashMap; +import java.util.Map; + +import com.iformall.douyin.pay.DouYinPayHelper; + +import lombok.Data; + +@Data +public class DouYinCreatePreOrder { + + private String appId;//小程序 AppID + private String sercrect;//密钥 + private String outOrderNo;//开发者侧的订单号, 同一小程序下不可重复 + private Integer totalAmount;//支付价格; 接口中参数支付金额单位为[分] + private String subject;//商品描述; 长度限制 128 字节,不超过 42 个汉字 + private String body;//商品详情 + private Integer validTime;//订单过期时间(秒); 最小 15 分钟,最大两天 + private String cpExtra;//开发者自定义字段,回调原样回传 【非必填】 + private String notifyUrl;//商户自定义回调地址 【非必填】 + private String thirdpartyId;//第三方平台服务商 id,非服务商模式留空 【非必填】 + private Integer disableMsg;//是否屏蔽担保支付的推送消息,1-屏蔽 0-非屏蔽,接入 POI 必传 【非必填】 + private String msgPage;//担保支付消息跳转页 【非必填】 + private String storeUid;//多门店模式下,门店 uid 【非必填】 + + public Map toRequestMap() { + Map map = new HashMap(); + map.put("app_id", appId); + map.put("out_order_no", outOrderNo); + map.put("total_amount", totalAmount); + map.put("subject", subject); + map.put("body", body); + map.put("valid_time", validTime); + map.put("cp_extra", cpExtra); + map.put("notify_url", notifyUrl); + map.put("thirdparty_id", thirdpartyId); + map.put("disable_msg", disableMsg); + map.put("msg_page", msgPage); + map.put("store_uid", storeUid); + + map.put("sign", DouYinPayHelper.createSign(map, sercrect)); + return map; + } +}