Skip to content

开始

UCC-PAY是领先的支付解决方案,提供支付、查询等功能。

HONGKONG支付API地址

https://shop.dannasmart.cn

API接口请求

必须采用application/json格式进行请求,统一采用POST,接口需要拼接验签POST参数

API返回格式

code=200表示请求成功,其它状态码均表示失败,如有返回的数据均在data参数中

{"code":200,"message":"返回的信息","data":{}}

公共验签POST参数

参数说明
keyAPIKEY
time接口调用时间戳(UTC+8),如果时间戳偏差超过60秒,则无法访问
nonce_str随机数,至少8位
sign签名

接口验签

签名方式,所有POST参数包含time、nonce_str、key进行ksort排列,排序好的参数加上secret,如nonceStr=2&time=3&key=4secret,然后进行32位MD5加密,最后get参数为:nonce_str=2&time=3&key=4&sign=xxxx

PHP DEMO

php
<?php

class Pay
{
    public function run()
    {
        $domain = 'https://xxxxxxx';

        //以获取接口主体信息为例,生成接口访问URL
        $key = 'xxxxx';
        $secret = 'xxxxx';
        $data = [];
        $data['key'] = $key;

        // 获取主体信息
//        $params = $this->encodeUrlParam($secret, $data);
//        $resp = $this->post($domain.'/externalApi/v1/company/get-info', $params);
//        echo $resp;

        // 支付请求
        $data['order_sn'] = "OT00001";
        $data['body'] = "测试01";
        $data['amount'] = 0.01;
        $data['notify_url'] = '回调通知url';
        $data['return_url'] = '支付完成跳转url';
        $params = $this->encodeUrlParam($secret, $data);
        $resp = $this->post($domain . '/externalApi/v1/pay/all-qrcode-pay', $params);
        echo $resp;
    }

    /**
     * 发送请求
     *
     * @param string $url
     * @param array $params
     * @return bool|string
     * @throws Exception
     */
    protected function post(string $url, array $params): bool|string
    {
        $json = json_encode($params, 320);

        $headers = [
            'Content-Type: application/json; charset=utf-8',
        ];
        $ch = curl_init();//初始化curl
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $data = curl_exec($ch);
        $err = curl_error($ch);
        $errno = curl_errno($ch);
        if ($errno) {
            $msg = 'curl errInfo: ' . $err . ' curl errNo: ' . $errno;
            throw new \Exception($msg);
        }
        curl_close($ch);
        return $data;
    }

    /**
     * 生成认证链接
     * @param string $secret secret
     * @param array $data 需要提交的数据,包含接口提供的key
     * @param string $signName 接口需要认证加密的参数值,默认为sign
     * @return mixed
     */
    protected function encodeUrlParam(string $secret, array $data = [], string $signName = 'sign'): mixed
    {
        if (isset($data[$signName])) {
            unset($data[$signName]);
        }
        $data['time'] = time();
        $data['nonce_str'] = $this->random(16);
        $params = $this->sortParams($data);
        $params .= $secret;
        $data[$signName] = strtolower(md5($params));
        return $data;
    }

    /**
     * 参数排序
     * @param array $params
     * @return string
     */
    protected function sortParams(array $params): string
    {
        ksort($params);
        $buff = "";
        foreach ($params as $k => $v) {
            if ($v != "" && !is_array($v)) {
                $buff .= $k . "=" . urlencode($v) . "&";
            }
        }
        return trim($buff, "&");
    }

    /**
     * 生成随机值
     * @param int $length
     * @param bool $numeric
     * @return string
     */
    protected function random(int $length, bool $numeric = false): string
    {
        $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
        $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed));

        $hash = '';
        if (!$numeric) {
            $hash = chr(rand(1, 26) + rand(0, 1) * 32 + 64);
            $length--;
        }

        $max = strlen($seed) - 1;
        $seed = str_split($seed);
        for ($i = 0; $i < $length; $i++) {
            $hash .= $seed[mt_rand(0, $max)];
        }

        return $hash;
    }
}

$pay = new Pay();
$pay->run();

JAVA DEMO

java
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class UrlParamEncoder {
    // 生成认证链接
    public Map<String, String> encodeUrlParam(String secret, Map<String, String> data, String signName) {
        if (data.containsKey(signName)) {
            data.remove(signName);
        }

        // 添加时间戳和随机数
        data.put("time", String.valueOf(System.currentTimeMillis() / 1000));  // 时间戳
        data.put("nonce_str", random(16));  // 随机字符串

        // 排序并生成签名
        String params = sortParams(data) + secret;
        data.put(signName, md5(params).toLowerCase());

        return data;
    }

    // 参数排序
    public String sortParams(Map<String, String> params) {
        return params.entrySet().stream()
                .filter(entry -> !entry.getValue().isEmpty() && !(entry.getValue() instanceof String && entry.getValue().equals("")))
                .sorted(Map.Entry.comparingByKey())
                .map(entry -> entry.getKey() + "=" + encode(entry.getValue()))
                .collect(Collectors.joining("&"));
    }

    // MD5 加密
    public String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hashBytes = md.digest(input.getBytes());
            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                hexString.append(String.format("%02x", b));
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    // URL 编码
    public String encode(String value) {
        try {
            return java.net.URLEncoder.encode(value, "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 生成随机字符串
    public String random(int length) {
        return random(length, false);
    }

    public String random(int length, boolean numeric) {
        String seed = numeric ? "0123456789" : "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder hash = new StringBuilder();
        for (int i = 0; i < length; i++) {
            int index = (int) (Math.random() * seed.length());
            hash.append(seed.charAt(index));
        }
        return hash.toString();
    }

    public static void main(String[] args) {
        UrlParamEncoder encoder = new UrlParamEncoder();
        String url = "https://xxxxx/externalApi/v1/company/get-info";
        String key = "xxxxx";
        String secret = "xxxxx";

        // 加密参数
        Map<String, String> data = new HashMap<>();
        data.put("key", key);
        data = encoder.encodeUrlParam(secret, data, "sign");

        JSONObject params = (JSONObject) JSONObject.toJSON(data);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
        String result = restTemplate.postForObject(url, new HttpEntity<>(params.toString(), headers), String.class);
        System.out.println("API Resp: " + result);
    }
}

UCC-PAY 版权所有