Authentication
Guide
- Alchemy Pay will issue a pair of merchantCode&privatekey for each partner once onboarded with Alchemy Pay. The merchantCode is used to identify the partner, while privatekey is for signature purposes.
- merchantCode&privatekey will be delivered to the partner upon request from Alchemy Pay's operation team. There is currently no online service for self-apply.
- Under no circumstances expose the privatekey in any API request.
Signature
Mutual authentication is implemented. For requests, the caller needs to compute a digital signature and add the signature as part of the HTTP body. Conversely, for responses, Alchemy Pay provides its signature in the HTTP body in the response. The request signature is generated as follows:
Step | Description | Example |
---|---|---|
1 | Sort all parameters in ascending order according to parameter names | Parameter list: abc=value1 bcd=value2 bad=value3 Sort result: abc=value1 bad=value3 bcd=value2 |
2 | Connect all parameters with '&' with stringA | abc=value1&bad=value3&bcd=value2 |
3 | Combine the stringA with ‘&’with key=privatekey to stringB. key is case sensitive must be with low case | abc=value1&bad=value3&bcd=value&key=privatekey. |
4 | MD5 the stingB with upper case | MD5(stringB).toUpperCase(); |
5 | Save the signature in HTTP body |
Sign demo
package com.achpay.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class Utils {
// private static final Logger logger = LoggerFactory.getLogger(Utils.class);
public static void main(String[] args) {
String key="[email protected]#";
Map map =new HashMap();
map.put("merchantCode","ME1657708812740");
map.put("unionId","9746630509196001");
map.put("crypto","24353");
System.out.println("========== sign =================: "+getSign(map,key));
}
/**
* sign=MD5(accountAttr=accountAttr&accountName=accountName&accountNo=accountNo&amount=amount&bankName=bankName&bankNumber=bankNumber&city=city&mchId=mchId&mchOrderNo=mchOrderNo¬ifyUrl=notifyUrl&province=province&remark=remark&reqTime=reqTime&key=key).toUpperCase()
* @param params
* @param privatekey
* @return
*/
public static String getSign(Map<String, Object> params, String privatekey) {
MapRemoveNullUtil.removeNullEntry(params);
Map<String, Object> map = new TreeMap<>();
map.putAll(params);
//以key1 = value1&key2 = value2
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<String, Object> s : map.entrySet()) {
String key = s.getKey();
Object value = s.getValue();
stringBuilder.append(key).append("=").append(value).append("&");
}
stringBuilder.append("key="+privatekey);
// logger.info("getSign{}", stringBuilder.toString());
String sign = MD5Util.getMd5(stringBuilder.toString().getBytes()).toUpperCase();
// logger.info("getSign-stringBuilder-sign为: {}, {}",stringBuilder, sign);
return sign;
}
/**
* @param obj
* @return
* @throws Exception
*/
public static Map<String, Object> objectToMap(Object obj) throws Exception {
if(obj == null)
return null;
Map<String, Object> map = new HashMap<String, Object>();
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor property : propertyDescriptors) {
String key = property.getName();
if (key.compareToIgnoreCase("class") == 0) {
continue;
}
Method getter = property.getReadMethod();
Object value = getter!=null ? getter.invoke(obj) : null;
map.put(key, value);
}
return map;
}
/**
* @param map
*/
public static void removeNullEntry(Map map){
removeNullKey(map);
removeNullValue(map);
}
/**
* @param map
* @return
*/
public static void removeNullKey(Map map){
Set set = map.keySet();
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
Object obj = (Object) iterator.next();
remove(obj, iterator);
}
}
/**
* @param map
* @return
*/
public static void removeNullValue(Map map){
Set set = map.keySet();
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
Object obj = (Object) iterator.next();
Object value =(Object)map.get(obj);
remove(value, iterator);
}
}
/**
* @param obj
* @param iterator
*/
private static void remove(Object obj,Iterator iterator){
if(obj instanceof String){
String str = (String)obj;
if(isEmpty(str.trim())){ //output map:{2=BB, 1=AA, 5=CC}
iterator.remove();
}
}else if(obj instanceof Collection){
Collection col = (Collection)obj;
if(col==null||col.isEmpty()){
iterator.remove();
}
}else if(obj instanceof Map){
Map temp = (Map)obj;
if(temp==null||temp.isEmpty()){
iterator.remove();
}
}else if(obj instanceof Object[]){
Object[] array =(Object[])obj;
if(array==null||array.length<=0){
iterator.remove();
}
}else{
if(obj==null){
iterator.remove();
}
}
}
public static boolean isEmpty(Object obj){
return obj == null || obj.toString().length() == 0 || obj.equals("null");
}
/**
*/
public static String getMd5(byte[] bytes) {
byte abyte0[];
MessageDigest messagedigest;
try {
messagedigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
throw new IllegalArgumentException("no md5 support");
}
messagedigest.update(bytes);
abyte0 = messagedigest.digest();
return byte2hex(abyte0);
}
/**
*/
public static String byte2hex(byte bytes[]) {
StringBuffer stringBuffer = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
if ((bytes[i] & 0xff) < 16) {
stringBuffer.append("0");
}
stringBuffer.append(Long.toString((long) bytes[i] & (long) 255, 16));
}
return stringBuffer.toString().toUpperCase();
}
}
Updated 3 months ago