签名算法


PAYJS 签名算法与微信官方签名算法一致


签名生成的通用步骤如下:

  1. 设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

  2. 在stringA最后拼接上&key=密钥得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值

特别注意以下重要规则:

  • 参数名ASCII码从小到大排序(字典序);

  • 如果参数的值为空不参与签名;

  • 参数名区分大小写;

  • 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。

  • PAYJS接口可能增加字段,验证签名时必须支持增加的扩展字段

举例


例如传递的参数如下:

mchid: 12345
total_fee: 1
out_trade_no: 123123123123

第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下

mchid=12345&out_trade_no=123123123123&total_fee=1

第二步:对上一步中的字符串拼接&key=密钥

mchid=12345&out_trade_no=123123123123&total_fee=1&key=xxxxxxxxx

第三步:对上一步中字符串取MD5值

$sign = md5('mchid=12345&out_trade_no=123123123123&total_fee=1&key=xxxxxxxxx');

第四步:对上面md5值转化为大写

$sign = strtoupper($sign);

代码示例

// 签名方法
function sign(array $data, $key) {
    ksort($data);
    $sign = strtoupper(md5(urldecode(http_build_query($data)).'&key='.$key));
    return $sign;
}

// 用法示例
$data = [
    'mchid' => '12345',
    'total_fee' => 1,
    'out_trade_no' => '123123123123',
];

// PAYJS通信密钥
$key = 'xxxxxxxxxxx';

$sign = sign($data, $key);

php

// 签名方法
function sign(array $data, $key) {
    ksort($data);
    $sign = strtoupper(md5(urldecode(http_build_query($data)).'&key='.$key));
    return $sign;
}

// 用法示例
$data = [
    'mchid' => '12345',
    'total_fee' => 1,
    'out_trade_no' => '123123123123',
];

// PAYJS通信密钥
$key = 'xxxxxxxxxxx';

$sign = sign($data, $key);

python

# !/usr/bin/env Python3
# -*- coding: utf-8 -*-

import hashlib
from urllib.parse import urlencode,unquote
'''
签名算法
'''
# 签名算法
def sign(attributes, payjs_key):
    attributes_new = {k: attributes[k] for k in sorted(attributes.keys())}
    sign_str = "&".join(
        [f"{key}={attributes_new[key]}" for key in attributes_new.keys()]
    )
    return (
        hashlib.md5((sign_str + "&key=" + payjs_key).encode(encoding="utf-8"))
        .hexdigest()
        .upper()
    )

# 用法示例
data = {
    'mchid' : '12345',
    'total_fee' : 1,
    'out_trade_no' : '123123123123'
}

# PAYJS通信密钥
key = 'xxxxxxxxxxx'

sign = sign(data, key)

go

package main

import (
    "crypto/md5"
    "encoding/hex"
    "fmt"
    "net/url"
    "sort"
    "strings"
)
// 签名算法
func sign(order map[string]string,key string)(sign string)  {
    data := url.Values{}
    for k,v :=range order{
        data.Add(k,v)
    }
    keys := make([]string, 0, 0)
    for key := range data{
        if data.Get(key) != ""{
            keys = append(keys,key)
        }
    }
    sort.Strings(keys)
    body := data.Encode()
    d,_ := url.QueryUnescape(body)
    d += "&key=" + key
    h := md5.New()
    h.Write([]byte(d))
    return  strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
}

func main()  {
    // 用法示例
    data := map[string]string{
        "mchid":"12345",
        "total_fee":"1",
        "out_trade_no":"123123123123"}

    // PAYJS通信密钥
    key := "xxxxxxxxxxx"

    sign := sign(data,key)

    fmt.Println(sign)

}

java

package com.hello.sign;
import org.springframework.util.DigestUtils;
import java.util.*;

public class Sign_java {
    //签名算法
    static class sign{
        String sign(Map<String, String> map,String key){
            StringBuilder sb = new StringBuilder();
            for(Map.Entry<String,String> entry : map.entrySet()){
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            sb.append("key=").append(key);
            return DigestUtils.md5DigestAsHex(sb.toString().getBytes()).toUpperCase();
        }
    }

    public static void main(String[] args) {
        // 用法示例
        Map<String, String> order = new TreeMap<String,String>();
        order.put("mchid", "12345");
        order.put("total_fee", "1");
        order.put("out_trade_no", "123123123123");
        order.put("... ...", "xxxxxx");
        ... ...

        // PAYJS通信密钥
        String key = "xxxxxxxxxxx";
        sign s = new sign();
        String sign = s.sign(order,key);
        System.out.println(sign);
    }

}

node

const crypto = require('crypto')

// PAYJS商户号和通信密钥
const mchid = 'xxxxxxxx'
const key = 'xxxxxxxxxx'

// 排序后转换为字符串
const toQueryString = (obj) => Object.keys(obj)
  .filter(key => key !== 'sign' && obj[key] !== undefined && obj[key] !== '')
  .sort()
  .map(key => {
    if (/^http(s)?:\/\//.test(obj[key])) { return key + '=' + encodeURI(obj[key]) } 
        else { return key + '=' + obj[key] }
  })
  .join('&')

// md5
const md5 = (str, encoding = 'utf8') => crypto.createHash('md5').update(str, encoding).digest('hex')

// 构造请求数据
let params = {
  'body': '商品名称',
  'total_fee': 1,
  'out_trade_no': '123456',
  'mchid': ''
}

params = toQueryString(params)
params += '&key=' + key

// 计算出最终签名
const sign = md5(params).toUpperCase()

console.log(sign)

c

#include <iostream>
#include <string>
#include <algorithm>
#include <map>
#include "md5.cpp"

using namespace std;
string sign(map<string,string> data,string key);
string md5(string strPlain);

int main ()
{
//  用法示例
    map<string,string> data = {
            { "mchid", "12345" },
            { "total_fee", "1" },
            { "out_trade_no", "123123123123" } };

//  PAYJS通信密钥
    const string key = "xxxxxxxxxxx";

    string  s  = sign(data,key);

    cout<<s<<endl;

    return 0;
}

//  签名方法
string sign(map<string,string> data,const string key){

    string s = "";

    while (!data.empty())
    {
        s+=data.begin()->first+"="+data.begin()->second+"&";
        data.erase(data.begin());
    }

    s+="key="+key;
    s = md5(s);
    transform(s.begin(),s.end(),s.begin(),::toupper);
    return s;
}

string md5(string strPlain)
{
    MD5_CTX mdContext;
    int bytes;
    unsigned char data[1024];

    MD5Init(&mdContext);
    MD5Update(&mdContext, (unsigned char*)const_cast<char*>(strPlain.c_str()), strPlain.size());
    MD5Final(&mdContext);

    string md5;
    char buf[3];
    for (int i = 0; i < 16; i++)
    {
        sprintf(buf, "%02x", mdContext.digest[i]);
        md5.append(buf);
    }
    return md5;
}

csharp

// 完整项目地址:https://github.com/payjs-cn/sdk-csharp

private Dictionary<string, string> sign(Dictionary<string, string> param)
{
    // 定义商户号mchid
    param.Add("mchid", "xxxxxxxxxxxxx");
    //去掉空的,排序
    Dictionary<string, string> newParam = param.Where(w => w.Value.Trim() != "").
        OrderBy(o => o.Key).ToDictionary(d => d.Key, d => d.Value);
    string paramStr = "";
    //拼接
    foreach (KeyValuePair<string, string> keyPair in newParam)
    {
        paramStr += (keyPair.Key + "=" + keyPair.Value + "&");
    }
    //加上通信密钥key
    paramStr += "key=" + "xxxxxxxxxxxxxxxx";
    //md5后大写
    string sign = (md5(paramStr)).ToUpper();

    newParam.Add("sign", sign);
    return newParam;
}

PAYJS提供了验签辅助工具可以在线验证签名准确性

powered by Gitbook最后更新: 2022-04-12