| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- package chinaums
- import (
- "crypto/hmac"
- "crypto/sha256"
- "encoding/base64"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "github.com/shopspring/decimal"
- "github.com/spf13/viper"
- "go_zh/model"
- "go_zh/pkg/utils"
- "sort"
- "strings"
- "sync"
- "time"
- )
- type ChinaumsServer struct {
- appId string
- appKey string
- appletId string //小程序appid
- wxAppId string //微信公众号appid
- signKey string //解密使用密文
- }
- var (
- chinaumsServerInstance *ChinaumsServer
- chinaumsServerOnce sync.Once
- )
- // GetOrderSnServerInstance 获取单例实例
- func GetOrderSnServerInstance() *ChinaumsServer {
- chinaumsServerOnce.Do(func() {
- chinaumsServerInstance = &ChinaumsServer{
- appId: viper.GetString("chinaums.appId"),
- appKey: viper.GetString("chinaums.appKey"),
- appletId: viper.GetString("chinaums.appletId"),
- wxAppId: viper.GetString("chinaums.wxAppId"),
- signKey: viper.GetString("chinaums.signKey"),
- }
- })
- return chinaumsServerInstance
- }
- // ShopOrderWxH5Pay 微信公众号店铺收款码支付
- func (cs *ChinaumsServer) ShopOrderWxH5Pay(shopMchInfo model.XShopMchid, orderSn string, payMoney decimal.Decimal, notifyUrl string, wxOpenid string) string {
- url := "https://api-mop.chinaums.com/v1/netpay/webpay/pay"
- result := make(map[string]interface{})
- result["requestTimestamp"] = time.Now().Format("2006-01-02 15:04:05")
- result["mid"] = shopMchInfo.SubMchid
- result["tid"] = shopMchInfo.Tid
- result["instMid"] = "YUEDANDEFAULT" // 业务类型 YUEDANDEFAULT
- result["merOrderId"] = orderSn
- result["notifyUrl"] = notifyUrl
- result["returnUrl"] = "https://shoppay.jhcs888.com/#/?order_sn=" + orderSn // 网页跳转地址
- result["totalAmount"] = payMoney.Mul(decimal.NewFromInt(100)).IntPart()
- result["subAppId"] = cs.wxAppId // 微信必填
- result["subOpenId"] = wxOpenid
- body, _ := json.Marshal(result)
- strUrl := cs.curlget(url, orderSn, string(body))
- return strUrl
- }
- // ShopOrderAliH5Pay 银联发起支付宝H5支付
- func (cs *ChinaumsServer) ShopOrderAliH5Pay(shopMchInfo model.XShopMchid, orderSn string, payMoney decimal.Decimal, notifyUrl string) string {
- url := "https://api-mop.chinaums.com/v1/netpay/trade/h5-pay"
- result := make(map[string]interface{})
- result["requestTimestamp"] = time.Now().Format("2006-01-02 15:04:05")
- result["mid"] = shopMchInfo.SubMchid
- result["tid"] = shopMchInfo.Tid
- result["merOrderId"] = orderSn
- result["instMid"] = "H5DEFAULT"
- result["notifyUrl"] = notifyUrl
- result["totalAmount"] = payMoney.Mul(decimal.NewFromInt(100)).IntPart()
- body, _ := json.Marshal(result)
- strUrl := cs.curlget(url, orderSn, string(body))
- return strUrl
- }
- // curlget 拼接请求地址
- func (cs *ChinaumsServer) curlget(url, orderSn, body string) string {
- timestamp := time.Now().Format("20060102150405")
- nonce := utils.Md5Sign(orderSn)
- signature := utils.UrlEncode(cs.getSignature(timestamp, nonce, body))
- reqUrl := fmt.Sprintf("%s?authorization=OPEN-FORM-PARAM&appId=%s×tamp=%s&nonce=%s&content=%s&signature=%s", url, cs.appId, timestamp, nonce, utils.UrlEncode(body), signature)
- return reqUrl
- }
- // buildSignStr 生成签名原始字符串,规则如下:
- // 1. 排除键名为 "sign" 的项;
- // 2. 排除值为 nil 的项;
- // 3. 剩余参数按键名升序排序;
- // 4. 值如果是 map 或 slice,则进行 JSON 编码;否则直接转换为字符串;
- // 5. 拼接成 key=value 形式,用 & 连接,末尾不加 &。
- func buildSignStr(params map[string]interface{}) string {
- // 收集所有非 "sign" 且值不为 nil 的键
- keys := make([]string, 0, len(params))
- for k, v := range params {
- if k == "sign" || v == nil {
- continue
- }
- keys = append(keys, k)
- }
- // 键名排序
- sort.Strings(keys)
- var builder strings.Builder
- for i, key := range keys {
- val := params[key]
- // 将值转换为字符串
- var valStr string
- switch v := val.(type) {
- case map[string]interface{}: // 关联数组 -> JSON 对象
- b, _ := json.Marshal(v)
- valStr = string(b)
- case []interface{}: // 索引数组 -> JSON 数组
- b, _ := json.Marshal(v)
- valStr = string(b)
- default:
- // 基本类型(string, int, float, bool 等)使用默认字符串表示
- valStr = fmt.Sprint(v)
- }
- builder.WriteString(key)
- builder.WriteString("=")
- builder.WriteString(valStr)
- // 除最后一个元素外,添加 & 分隔符
- if i < len(keys)-1 {
- builder.WriteString("&")
- }
- }
- return builder.String()
- }
- // getSignature 生成签名,
- // 参数 timestamp, nonce, body 均为字符串类型
- func (cs *ChinaumsServer) getSignature(timestamp, nonce, body string) string {
- // 1. 计算 body 的 SHA256 哈希(原始二进制)并转换为十六进制字符串
- bodyHash := sha256.Sum256([]byte(body))
- bodyHex := hex.EncodeToString(bodyHash[:])
- // 2. 拼接 appid + timestamp + nonce + bodyHex
- message := cs.appId + timestamp + nonce + bodyHex
- // 3. 使用 HMAC-SHA256 对消息进行签名,密钥为 appkey
- h := hmac.New(sha256.New, []byte(cs.appKey))
- h.Write([]byte(message))
- signature := h.Sum(nil) // 原始二进制签名
- // 4. 对签名结果进行 Base64 编码
- return base64.StdEncoding.EncodeToString(signature)
- }
|