1、前置
1、沙特的的一个支付
2、案列采用java编写
3、网站应该是这个吧, 因为我这边是通过一个PDF文档开发的, https://www.arabbank.com/ar/mainmenu/home
4、介最后贴图验证以及做好后的样子
5、流程: 创建订单 —> 回调判断(支付成功/失败)
6、代码已上传码云,如果起到帮助还请star一下地址:https://gitee.com/xmaxm/payments_hodgepodge
7、开发时使用的文档(因为文档里面可能涉及了一些关键的信息, 就不贴文档上来了): ARB Payment Gateway REST API Integration Doc_V1.11.pdf
8、一些很重要的截图
2、准备工作
没有, 因为没有登录过, 只有一个PDF
需要说明一点, 金额, 1就是1, 不需要*100
工具类:
package com.tomorrow.payments_hodgepodge.util;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* @author Tomorrow
* @date 2020/11/10 17:27
*/
public class ARBUtil {
public static String AES_IV = "PGKEYENCDECIVSPC";
public static String encryptAES(String key, String encryptString) {
byte[] encryptedText = null;
IvParameterSpec ivspec = null;
SecretKeySpec skeySpec = null;
Cipher cipher = null;
byte[] text = null;
String s = null;
try {
ivspec = new IvParameterSpec(AES_IV.getBytes("UTF-8"));
skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
text = encryptString.getBytes("UTF-8");
encryptedText = cipher.doFinal(text);
s = byteArrayToHexString(encryptedText);
} catch (Exception e) {
e.printStackTrace();
} finally {
encryptedText = null;
ivspec = null;
skeySpec = null;
cipher = null;
text = null;
}
return s.toUpperCase();
}
public static String decryptAES(String key, String encryptedString) {
SecretKeySpec skeySpec = null;
IvParameterSpec ivspec = null;
Cipher cipher = null;
byte[] textDecrypted = null;
try {
byte[] b = hexStringToByteArray(encryptedString);
skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
ivspec = new IvParameterSpec(AES_IV.getBytes("UTF-8"));
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, skeySpec, ivspec);
textDecrypted = cipher.doFinal(b);
} catch (Exception var10) {
var10.printStackTrace();
} finally {
skeySpec = null;
ivspec = null;
cipher = null;
}
return new String(textDecrypted);
}
public static String byteArrayToHexString(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i != data.length; ++i) {
int v = data[i] & 255;
buf.append("0123456789abcdef".charAt(v >> 4));
buf.append("0123456789abcdef".charAt(v & 15));
}
return buf.toString();
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
}
return data;
}
}
3、ARB 创建订单
public CommonResult createOrder(String orderNo) {
JSONObject jsonObject = new JSONObject();
// 跨门户ID。商家下载相同的商家门户网站
jsonObject.put("id", tranportalId);
// 支付网关发送的商家成功URL 通知请求。
jsonObject.put("responseURL", callback);
// 支付网关发送的商家错误URL 在处理事务时发生任何错误时响应。
jsonObject.put("errorURL", callback);
JSONObject requestParams = new JSONObject();
// 交易金额
requestParams.put("amt", "12.00");
// 定义事务操作 采购:1 退款:2 无效:3 调查:8
requestParams.put("action", "1");
// Tranportal密码。在商家门户下载相同的商家。
requestParams.put("password", password);
// 跨门户ID。商家下载相同的商家门户网站
requestParams.put("id", tranportalId);
// KSA的3位货币代码例:682
requestParams.put("currencyCode", "682");
// 商户唯一参考编号
requestParams.put("trackId", orderNo);
// 用户(商家)定义这些字段。现场数据为 与事务请求一起传递,然后返回 在事务响应中。商户应确保 字段在不需要传递数据时留空。
requestParams.put("udf1", "udf1text");
// requestParams.put("udf2", "udf2text");
// requestParams.put("udf3", "udf3text");
// requestParams.put("udf4", "udf4text");
// requestParams.put("udf5", "udf5text");
// 支付网关发送的商家成功URL 通知请求。
requestParams.put("responseURL", callback);
// 支付网关发送的商家错误URL 在处理事务时发生任何错误时响应。
requestParams.put("errorURL", callback);
// JSONObject accountDetailsParams = new JSONObject();
// // 8位银行识别码
// accountDetailsParams.put("bankIdCode", "1****");
// // 24位伊班数字
// accountDetailsParams.put("iBanNum", "5678****76254");
// // 服务数量, 服务金额
// accountDetailsParams.put("serviceAmount", "200.00");
// // 日期格式:YYYYDDMM
// accountDetailsParams.put("valueDate", "20203112");
//
// List<JSONObject> list = new ArrayList<>();
// list.add(accountDetailsParams);
// // 条件如果商人选择未来支付。 分开付款细节。
// requestParams.put("accountDetails", list);
List<JSONObject> requestEncryptAES = new ArrayList<>();
requestEncryptAES.add(requestParams);
String encryptAES = ARBUtil.encryptAES(key, JSONObject.toJSONString(requestEncryptAES));
jsonObject.put("trandata", encryptAES);
// 请求数据
List<JSONObject> requestBody = new ArrayList<>();
requestBody.add(jsonObject);
Map<String, String> map = new HashMap<>();
map.put("content-type", "application/json");
try {
String responseParams = HttpsUtils.HttpsRequest(BANK_HOSTED_URL, "POST", JSONObject.toJSONString(requestBody), map);
JSONArray jsonArray = JSONObject.parseArray(responseParams);
JSONObject object = (JSONObject) jsonArray.get(0);
String status = object.getString("status");
if ("1".equals(status)) {
String result = object.getString("result");
String[] split = result.split(":");
String requestUrl = split[1] + ":" + split[2] + "?PaymentID=" + split[0];
return CommonResult.success("SUCCESS", requestUrl);
}
return CommonResult.failMessage(500, "FAIL", object);
} catch (Exception e) {
e.printStackTrace();
}
return CommonResult.fail(500, "FAIL");
}
4、ARB 回调
public CommonResult callback(HttpServletRequest request) {
// //获取所有参数的map集合
// Map<String, String[]> parameterMap = request.getParameterMap();
// //遍历
// Set<String> keySet = parameterMap.keySet();
// for (String name : keySet) {
// //根据键获取值
// String[] values = parameterMap.get(name);
// System.out.println(name);
// for (String value : values) {
// System.out.println(value);
// }
// System.out.println("------------");
// }
String paymentid = request.getParameter("paymentid");
String trackid = request.getParameter("trackid");
String custid = request.getParameter("custid");
String trandata = request.getParameter("trandata");
// 参数解密
String decryptAES = ARBUtil.decryptAES(key, trandata);
try {
decryptAES = URLDecoder.decode(decryptAES, "UTF-8");
log.info("解密后的数据: {}", decryptAES);
JSONArray jsonArray = JSONObject.parseArray(decryptAES);
JSONObject jsonObject = (JSONObject) jsonArray.get(0);
if ("CAPTURED".equals(jsonObject.getString("result"))) {
// transId 退款ID
return CommonResult.success("SUCCESS", jsonObject.getString("transId"));
}
if (jsonObject.getString("result") != null) {
return CommonResult.failMessage(500, "FAIL", jsonObject.getString("result"));
} else {
return CommonResult.failMessage(500, "FAIL", jsonObject.getString("errorText"));
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return CommonResult.fail(500, "FAIL");
}
5:退款
退款接口是上面截图的 Merchant Hosted对应的请求地址
@Override
public CommonResult arbRefund(String amount, String refundOrderNo) {
JSONObject jsonObject = new JSONObject();
// 跨门户ID。商家下载相同的商家门户网站
jsonObject.put("id", tranportalId);
JSONObject requestParams = new JSONObject();
// 跨门户ID。商家下载相同的商家门户网站
requestParams.put("id", tranportalId);
// Tranportal密码。在商家门户下载相同的商家。
requestParams.put("password", password);
// 定义事务操作 采购:1 退款:2 无效:3 调查:8
requestParams.put("action", "2");
// 交易金额
requestParams.put("amt", amount);
// KSA的3位货币代码例:682
requestParams.put("currencyCode", "682");
// 退款transId支付成功后返回的数据
requestParams.put("transId", refundOrderNo);
List<JSONObject> requestEncryptAES = new ArrayList<>();
requestEncryptAES.add(requestParams);
String encryptAES = ARBUtil.encryptAES(key, JSONObject.toJSONString(requestEncryptAES));
jsonObject.put("trandata", encryptAES);
// 请求数据
List<JSONObject> requestBody = new ArrayList<>();
requestBody.add(jsonObject);
Map<String, String> map = new HashMap<>();
map.put("content-type", "application/json");
try {
String responseParams = HttpsUtils.HttpsRequest(MERCHANT_HOSTED_URL, "POST", JSONObject.toJSONString(requestBody), map);
JSONArray jsonArray = JSONObject.parseArray(responseParams);
JSONObject object = (JSONObject) jsonArray.get(0);
String status = object.getString("status");
if ("1".equals(status)) {
// 参数解密
String decryptAES = ARBUtil.decryptAES(key, object.getString("trandata"));
decryptAES = URLDecoder.decode(decryptAES, "UTF-8");
log.info("解密后的数据: {}", decryptAES);
return CommonResult.success("SUCCESS", object);
}
return CommonResult.failMessage(500, "FAIL", object);
} catch (Exception e) {
e.printStackTrace();
}
return CommonResult.fail(500, "FAIL");
}
6:杂谈
1、代码上传码云地址:https://gitee.com/xmaxm/payments_hodgepodge
2、创建订单成功后, 会返回一个连接,
①请求这个链接进行支付
②ARB会请求我们的回调地址
③回调接口返回数据
④ARB响应第一步
3、测试卡:
4、文档截图如下:
提供一个群:807770565,欢迎各位进来尬聊 (人不多, 进了就不要退了, 要不就不进, 跪求留一点人, 人多了就活跃了, 跪谢)