1:在平台申请支付功能,设置回调接口,token
2:服务端支付下单
加密工具类
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
*加密工具类
*/
public class DouYinUtil {
// 支付密钥值
private static final String SALT = "担保交易设置中的SALT";
// paramsMap 参数含义解释同 golang
public static String getSign(Map<String, Object> paramsMap) {
List<String> paramsArr = new ArrayList<>();
for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
String key = entry.getKey();
if (key.equals("other_settle_params")) {
continue;
}
String value = entry.getValue().toString();
value = value.trim();
if (value.startsWith("\"") && value.endsWith("\"") && value.length() > 1) {
value = value.substring(1, value.length() - 1);
}
value = value.trim();
if (value.equals("") || value.equals("null")) {
continue;
}
switch (key) {
case "app_id":
case "thirdparty_id":
case "sign":
break;
default:
paramsArr.add(value);
break;
}
}
paramsArr.add(SALT);
Collections.sort(paramsArr);
StringBuilder signStr = new StringBuilder();
String sep = "";
for (String s : paramsArr) {
signStr.append(sep).append(s);
sep = "&";
}
return md5FromStr(signStr.toString());
}
public static String md5FromStr(String inStr) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
byte[] byteArray = inStr.getBytes(StandardCharsets.UTF_8);
byte[] md5Bytes = md5.digest(byteArray);
StringBuilder hexValue = new StringBuilder();
for (byte md5Byte : md5Bytes) {
int val = ((int) md5Byte) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
public static String getSha1(String str) {
if (str == null || str.length() == 0) {
return null;
}
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes(PublicFinal.S_UTF_8));
byte[] md = mdTemp.digest();
int j = md.length;
char[] buf = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
// TODO: handle exception
return null;
}
}
}
支付下单/支付回调
@Autowired
private RestTemplate restTemplate;
/**
* 获取支付的请求参数
*/
@ApiOperation(value = "获取支付的请求参数")
@GetMapping("prepay")
public Object payPrepay(Integer orderId) {
logger.info("---------orderId:{}",orderId);
Map<String, Object> parame = new LinkedHashMap<>();
//抖音小程序app
parame.put("app_id", "appid");
// 商户订单编号
parame.put("out_order_no", "订单号,需平台唯一");
//支付金额
parame.put("total_amount", new BigDecimal(1).multiply(new BigDecimal(100)).intValue());
// 商品标题
parame.put("subject","商品名称");
// 商品描述(不能为空)
parame.put("body", "商品描述");
// 回调地址(与支付设置中一致)
parame.put("notify_url", "担保交易设置中的回调地址");
// 时间
parame.put("valid_time",900);
// 支付密钥
String sign = DouYinUtil.getSign(parame);
logger.info("支付下单密钥签名:{}",sign);
// 数字签证
parame.put("sign", sign);
String res = restTemplate.postForObject("https://developer.toutiao.com/api/apps/ecpay/v1/create_order",parame,String.class);
logger.info("res-----------:{}",res);
JSONObject sessionData1 = JSON.parseObject(res);
if ("0".equalsIgnoreCase(sessionData1.getString("err_no"))) {
logger.info("支付成功");
JSONObject sessionData = sessionData1.getJSONObject("data");
Map<String, Object> resultObj = new HashMap();
String prepayId = sessionData.getString("order_id");
resultObj.put("err_no",sessionData1.getString("err_no"));
resultObj.put("order_id",prepayId);
resultObj.put("order_token",sessionData.getString("order_token"));
return resultObj;
}else{
logger.info("支付失败");
Map<String, Object> resultObj = new HashMap<>();
resultObj.put("err_no",sessionData1.getString("err_no"));
return resultObj;
}
}
@ApiOperation(value = "抖音微信订单回调接口")
@RequestMapping(value = "/notify", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
@ResponseBody
public Object dynotify(HttpServletRequest request, HttpServletResponse response) {
try {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
InputStream in = request.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.close();
in.close();
String responseJson = new String(out.toByteArray(),"utf-8");
logger.info("responseJson:{}",responseJson);
JSONObject jsonObject = JSONObject.parseObject(responseJson);
JSONObject msgJson = jsonObject.getJSONObject("msg");
String resultCode = jsonObject.getString("type");
if ("payment".equalsIgnoreCase(resultCode)) {
List<String> sortedString = new ArrayList<>();
//token
sortedString.add("token");
//时间戳
sortedString.add(jsonObject.getString("timestamp"));
//随机数
sortedString.add(jsonObject.getString("nonce"));
//msg
sortedString.add(jsonObject.getString("msg"));
Collections.sort(sortedString);
StringBuilder sb = new StringBuilder();
sortedString.forEach(sb::append);
String msg_signature = jsonObject.getString("msg_signature");
String sign = DouYinUtil.getSha1(sb.toString().trim());
logger.info("支付回调接口密钥签名:{}",sign);
if(!sign.equals(msg_signature)) {//判断签名
JSONObject resultJson = new JSONObject();
resultJson.put("err_no",8);
resultJson.put("err_tips","error");
return resultJson.toString();
}
JSONObject resultJson = new JSONObject();
resultJson.put("err_no",0);
resultJson.put("err_tips","success");
return resultJson.toString();
} else {
//订单编号
String outTradeNo = msgJson.getString("cp_orderno");
logger.error("订单" + outTradeNo + "支付失败");
JSONObject resultJson = new JSONObject();
resultJson.put("err_no",0);
resultJson.put("err_tips","error");
return resultJson.toString();
}
} catch (Exception e) {
e.printStackTrace();
JSONObject resultJson = new JSONObject();
resultJson.put("err_no",8);
resultJson.put("err_tips","error");
return resultJson.toString();
}
}
注意:支付下单签名加密方式为Md5,支付回调接口签名为Sha1,加密参数顺序不能乱