新增luckinpay支付

fix/release
mofangmin 1 year ago
parent a4b59ae85b
commit 54df366cd8
  1. 3
      modules/pay/allpay/all.go
  2. 107
      modules/pay/base/signrsa.go
  3. 165
      modules/pay/luckyinpay/base.go
  4. 90
      modules/pay/luckyinpay/values.go
  5. 15
      modules/pay/payplus/base.go
  6. 1
      modules/pay/values/values.go

@ -5,6 +5,7 @@ import (
"server/modules/pay/base"
"server/modules/pay/gopay"
"server/modules/pay/grepay"
"server/modules/pay/luckyinpay"
"server/modules/pay/mlpay"
"server/modules/pay/moonpay2"
"server/modules/pay/payplus"
@ -57,6 +58,7 @@ type AllPay struct {
CamelPay func(b *base.Base)
Moonpay2 func(b *base.Base)
PayPlus func(b *base.Base)
LuckyinPay func(b *base.Base)
}
var All = &AllPay{}
@ -67,6 +69,7 @@ func init() {
All.GrePay = grepay.NewSub
All.MLPay = mlpay.NewSub
All.PayPlus = payplus.NewSub
All.LuckyinPay = luckyinpay.NewSub
}
func NewSub(b *base.Base, index int) {

@ -11,6 +11,7 @@ import (
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"math/big"
"github.com/liangdas/mqant/log"
@ -132,12 +133,12 @@ func RsaSignSha1(data []byte, privateKey string) ([]byte, error) {
h := sha1.New()
h.Write(data)
hashed := h.Sum(nil)
//获取私钥
// 获取私钥
block, _ := pem.Decode([]byte(privateKey))
if block == nil {
return nil, errors.New("private key error")
}
//解析PKCS1格式的私钥
// 解析PKCS1格式的私钥
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
@ -155,11 +156,11 @@ func RsaSignVer(data []byte, signature, publicKey string) error {
// 类型断言
pub := pubInterface.(*rsa.PublicKey)
//验证签名
// 验证签名
return rsa.VerifyPKCS1v15(pub, crypto.SHA1, hashed[:], sign)
}
func RSA_public_decrypt(pubKey *rsa.PublicKey, data []byte) []byte {
func RsaPublicDecrypt(pubKey *rsa.PublicKey, data []byte) []byte {
c := new(big.Int)
m := new(big.Int)
m.SetBytes(data)
@ -192,7 +193,7 @@ func RsaDecode(signature, publicKey string) error {
// 因为加密解密用rsa有长度限值,因此这个地方就看具体限值的多少了
for len(decodeBytes) > 0 {
decodePart := decodeBytes[:128]
plain := RSA_public_decrypt(public.(*rsa.PublicKey), decodePart)
plain := RsaPublicDecrypt(public.(*rsa.PublicKey), decodePart)
result += string(plain)
decodeBytes = decodeBytes[128:]
}
@ -203,16 +204,62 @@ func RsaDecode(signature, publicKey string) error {
return errors.New("check sign fail")
}
// 私钥加密
func SignRSA(data string, privateKey string) (string, error) {
gRsa := gorsa.RSASecurity{}
gRsa.SetPrivateKey(privateKey)
rsaData, err := gRsa.PriKeyENCTYPT([]byte(data))
func SignRSA(prvkey []byte, hash crypto.Hash, data []byte) (string, error) {
block, _ := pem.Decode(prvkey)
if block == nil {
return "", errors.New("decode private key error")
}
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(rsaData), nil
// MD5 and SHA1 are not supported as they are not secure.
var hashed []byte
switch hash {
case crypto.SHA224:
h := sha256.Sum224(data)
hashed = h[:]
case crypto.SHA256:
h := sha256.Sum256(data)
hashed = h[:]
case crypto.SHA384:
h := sha512.Sum384(data)
hashed = h[:]
case crypto.SHA512:
h := sha512.Sum512(data)
hashed = h[:]
}
signature, _ := rsa.SignPKCS1v15(rand.Reader, privateKey.(*rsa.PrivateKey), hash, hashed)
return base64.StdEncoding.EncodeToString(signature), nil
}
func RsaVerify(pubkey []byte, hash crypto.Hash, data, sig []byte) error {
block, _ := pem.Decode(pubkey)
if block == nil {
return errors.New("decode public key error")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return err
}
// SHA1 and MD5 are not supported as they are not secure.
var hashed []byte
switch hash {
case crypto.SHA224:
h := sha256.Sum224(data)
hashed = h[:]
case crypto.SHA256:
h := sha256.Sum256(data)
hashed = h[:]
case crypto.SHA384:
h := sha512.Sum384(data)
hashed = h[:]
case crypto.SHA512:
h := sha512.Sum512(data)
hashed = h[:]
}
return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), hash, hashed, sig)
}
func SignRSADecode(data string, publicKey string) (string, error) {
@ -233,3 +280,39 @@ func SignRSADecode(data string, publicKey string) (string, error) {
return string(rsaData), nil
}
// 解析 PKCS#8 格式的私钥
func parsePrivateKey(pemStr string) (*rsa.PrivateKey, error) {
block, _ := pem.Decode([]byte(pemStr))
if block == nil || block.Type != "PRIVATE KEY" {
return nil, fmt.Errorf("failed to decode PEM block containing private key")
}
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
privateKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("not an RSA private key")
}
return privateKey, nil
}
// 解析公钥
func parsePublicKey(pemStr string) (*rsa.PublicKey, error) {
block, _ := pem.Decode([]byte(pemStr))
if block == nil || block.Type != "PUBLIC KEY" {
return nil, fmt.Errorf("failed to decode PEM block containing public key")
}
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
publicKey, ok := pubKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("not an RSA public key")
}
return publicKey, nil
}

@ -0,0 +1,165 @@
package luckyinpay
import (
"crypto"
"encoding/base64"
"errors"
"fmt"
"net/http"
"server/common"
"server/modules/pay/base"
"server/modules/pay/values"
"server/pb"
"strconv"
"strings"
"time"
"github.com/gogo/protobuf/proto"
"github.com/liangdas/mqant/log"
)
func NewSub(b *base.Base) {
sub := &Sub{
Base: b,
}
b.HttpType = base.HttpTypeJson
b.ShouldSignUpper = true
b.Channel = values.LuckyinPay
if b.Opt == base.OPTPay {
b.Resp = new(PayWithdrawResponse)
b.ReqURL = payApi
} else if b.Opt == base.OPTWithdraw {
b.Resp = new(PayWithdrawResponse)
b.ReqURL = withdrawApi
} else if b.Opt == base.OPTPayCB {
b.SignPassStr = []string{"sign"}
b.CallbackResp.Msg = strings.ToLower(statusSuccess)
b.CallbackReq = new(CallbackReq)
} else if b.Opt == base.OPTWithdrawCB {
b.SignPassStr = []string{"sign", "msg"}
b.CallbackResp.Msg = strings.ToLower(statusSuccess)
b.CallbackReq = new(CallbackReq)
}
b.Sub = sub
}
type Sub struct {
Base *base.Base
}
func (s *Sub) PackHeader(_ http.Header) {
// header.Set("Merchant-Id", config.GetConfig().Pay.IGeek.MID)
}
func (s *Sub) PackReq() interface{} {
if s.Base.Opt == base.OPTPay {
return s.PackPayReq()
}
return s.PackWithdrawReq()
}
func (s *Sub) GetResp() (proto.Message, error) {
if s.Base.Opt == base.OPTPay {
resp := s.Base.Resp.(*PayWithdrawResponse)
if resp.Message != statusSuccess || resp.Data.PayData == "" {
return nil, errors.New("pay fail")
}
return &pb.InnerRechargeResp{APIOrderID: fmt.Sprintf("%v", resp.Data.PayOrderNo), URL: resp.Data.PayData, Channel: uint32(values.LuckyinPay)}, nil
}
resp := s.Base.Resp.(*PayWithdrawResponse)
if s.Base.Status == 0 && resp.Message != statusSuccess {
return nil, errors.New("withdraw fail")
}
return &pb.InnerWithdrawResp{APIOrderID: fmt.Sprintf("%v", resp.Data.PayOrderNo), Channel: uint32(values.LuckyinPay)}, nil
}
func (s *Sub) PackPayReq() interface{} {
r := s.Base.PayReq
send := &PayReq{
MchNo: mchId,
MchOrderNo: r.OrderID,
Currency: "INR",
PayAmount: fmt.Sprintf("%."+strconv.Itoa(2)+"f", float64(r.Amount)/common.DecimalDigits),
AccountName: r.Name,
AccountEmail: r.Email,
AccountPhone: r.Phone,
CustomerIp: r.IP,
NotifyUrl: values.GetPayCallback(values.LuckyinPay),
SuccessPageUrl: "https://www.tp7k.com",
Summary: "",
ReqTime: fmt.Sprintf("%d", time.Now().UnixMilli()),
}
signStr := base.GetSignStr(send, s.Base.SignPassStr...)
sign, err := base.SignRSA(privateKey, crypto.SHA256, []byte(signStr))
if err != nil {
log.Error("err:%v", err)
}
send.Sign = sign
return send
}
func (s *Sub) PackWithdrawReq() interface{} {
r := s.Base.WithdrawReq
if r.PayType != int64(common.PayTypeBank) {
return nil
}
accountType := ""
if common.PayType(r.PayType) == common.PayTypeBank {
accountType = "IFSC"
} else if common.PayType(r.PayType) == common.PayTypeUPI {
accountType = "UPI"
}
send := &WithdrawReq{
MchNo: mchId,
MchOrderNo: r.OrderID,
Currency: "INR",
PayAmount: fmt.Sprintf("%."+strconv.Itoa(2)+"f", float64(r.Amount)/common.DecimalDigits),
AccountType: accountType,
AccountCode: r.PayCode,
AccountNo: r.CardNo,
AccountName: r.Name,
AccountEmail: r.Email,
AccountPhone: r.Phone,
CustomerIp: r.IP,
NotifyUrl: values.GetWithdrawCallback(values.LuckyinPay),
ReqTime: fmt.Sprintf("%d", time.Now().UnixMilli()),
}
signStr := base.GetSignStr(send, s.Base.SignPassStr...)
sign, err := base.SignRSA(privateKey, crypto.SHA256, []byte(signStr))
if err != nil {
log.Error("err:%v", err)
}
send.Sign = sign
return send
}
func (s *Sub) CheckSign(_ string) bool {
if s.Base.Opt == base.OPTPayCB {
req := s.Base.CallbackReq.(*CallbackReq)
log.Debug("checkSign pay:%+v", *req)
s.Base.CallbackResp.OrderID = req.MchOrderNo
s.Base.CallbackResp.Success = req.PayState == 2
signStr := base.GetSignStr(req, s.Base.SignPassStr...)
sign, _ := base64.StdEncoding.DecodeString(req.Sign)
err := base.RsaVerify(publicKey, crypto.SHA256, []byte(signStr), sign)
if err != nil {
log.Error("err:%v", err)
}
return err == nil
} else if s.Base.Opt == base.OPTWithdrawCB {
req := s.Base.CallbackReq.(*CallbackReq)
log.Debug("checkSign withdraw:%+v", *req)
s.Base.CallbackResp.OrderID = req.MchOrderNo
s.Base.CallbackResp.Success = req.PayState == 2
s.Base.CallbackResp.APIOrderID = req.PayOrderNo
s.Base.CallbackResp.FailMessage = req.ErrMsg
signStr := base.GetSignStr(req, s.Base.SignPassStr...)
sign, _ := base64.StdEncoding.DecodeString(req.Sign)
err := base.RsaVerify(publicKey, crypto.SHA256, []byte(signStr), sign)
if err != nil {
log.Error("err:%v", err)
}
return err == nil
}
return false
}

@ -0,0 +1,90 @@
package luckyinpay
// 常量定义
const (
statusSuccess = "SUCCESS"
statusFail = "FAIL"
payApi = "https://api.luckyinpay.xyz/api/pay/createPayinOrder"
withdrawApi = "https://api.luckyinpay.xyz/api/pay/createPayoutOrder"
mchId = "M1723690026"
)
var privateKey = []byte(`-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCaOiesI2CY1qG6WxCnYbzI4WRmJrOTWnagPBm4YGjP5HaKlYNGeoRQKwrd6nm7KdyUCAg4LW2p/5NINXquLAGLkDcPUzTEaK36JiJd46lyTw2a+LQAtXMXq6PLM8MVU5yrR7Ndqj8/6SYjIaH6xE7yZq7YioAZoqENfn2Nzz7i5JY5rAi9DOjJ2Ti2lIkV/I6eaCpJecp9DgDMfGsTK4Dbh26uHgFaQ9ruwLP437V+h1x7QkEF1mzpqTgtXV7+opLgGX0fpdT8GabBoAEqQL41rxWQo3oVnV3A69eW2t16/LFMZLFVp7tzFzv4iRDehZuAVHFw7bykbaZ9omEy00cfAgMBAAECggEAK+S5PcijyVvsk3kveDPc4t8jD9+b5Cgu8tOoDk2CBwjio/aBciGUXqyhXx9InfgACzFuBW8IcwWGCVsPG1ry5aGZygbIUc3pFfSce1Q7+Yh+Osjlkf4ST35OE5sbvooZYLX7WoEFl07nx7/etdwBDAQlZwOkCae7ZnfFjAKbxz8OTz0ZLH68LKYBB51KvSrto+ZJDzSmqMV/21P9JwpZklvG+oNmA1D6fYOdz0QcKpewcJbmZ4JsPdVOy9zyUVCFhra+5P5IlI07x5tWAFZmktiXGKC+a+gLrqR96FJ+l/SjeMWkhFEiGuFBDTNvfpVHxNY30i5YQ6b1zNDmZZ/AgQKBgQDLlgEeImy6/iVin3jecZuzsgbQiB9SuyrIjziBzlxMTMRgNuGmwCX5ScmObxpwzCKwN/r24wX9HB92+vPXG3CuWRvLCy53iTfg4K08CIhR5qZKjK7XknMN+c16JA9jD358MIol3PhY5qdfV2WEoDRioEGPooNrP4Ka6SoSc/GlLwKBgQDB7v+7UCCU2hyCEhSNWLPvhuzy6BbqNHZYA+WH8aRt+T/9vqDefXj9uBjBQC2IIdfB1bTiZXOonwQsdLw8UEw0sTSg9GNqJY60TJCkJCFoSK1pjP0oS6CSjfcMg6TJ07RKxmnfKt5muIfarJs4jDXLbhLRcMfVnx+CjUu9R7fhEQKBgCxVvmiRlpwiPIH9uJS4qO+5FULUYf3iRK7ogaXDCtaS4TESwpFNxvdC2mHqUBfAXM2DY2PgjK1SMof2nqN5llBhl2vgQ35Eo6aVRySLtyQe1pfliy3m9qdUfyraL3+jIChraapVNMAnesuIfNMlEENqItzkxznT2t1haYgbN8SBAoGAKUAg/ULJ61+JeNFHtdkMTQKyG+jqFt8Z3RbXF9d6VjoSNnL53NlrhuxFt7Vi+fXY4s4GL7HMrC1Gwf4CnAGoc/zNaP8Stl1OwWMX0RIuWPQ8jpsCrjOH8CRqTCskPhnd25ngoyuHKupNW2Ijkh+gwaikMBCBMRsrpKVRWu2KW0ECgYEAw3ddkjrPmvqXAL1l8M/vymxsYFO6HpRunf7zknwOZDETfqm/0uJhuH8YJqQRnsgUxTgealid9M2hgO0L/IcqyA7/AoCP5FHFB82kmcBV5Nz0+jB0gGKw222foCLTuyjv0GxL3DgoYjASFgJUHICsc1scCc5rVd45uJgUbtR/g6k=
-----END PRIVATE KEY-----
`)
var publicKey = []byte(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjOrF8H78wLlVUd0Xe3qe+mBvkzchxccuBqtPZGbK9ebhXbqOaIgRQByjWSuC1/qJcT6eBumSX/iHUVaTdf3v2XO1QCbUZZm74vcSsO5f64bmzTtk/cLa1S4068Rfde0YoX/avNfiX2nRz1SBzy3zaiwA8pmhkbpU+L3k+a8qd3WH3Hq2MpX/dD1haqzTHYiA8+xJANixvNtKWq5AMyrs9rSkwGTEHV33ETLUZgpHjtgI+L/PAqzIaW89dgh4EIga4sNGr/3iUkZU1vYKj2PHY6/pfgs5wCS0BhxHF35LYCHgyZV7UH4oxu0kOtGGmdOAeIylCWRXdPkaXGGnOlmYmQIDAQAB
-----END PUBLIC KEY-----`)
// PayReq 代收下单请求结构体
type PayReq struct {
MchNo string `json:"mchNo"` // 商户号
MchOrderNo string `json:"mchOrderNo"` // 商户订单号
Currency string `json:"currency"` // 币种
PayAmount string `json:"payAmount"` // 支付金额
AccountName string `json:"accountName"` // 客户姓名
AccountEmail string `json:"accountEmail"` // 客户邮箱
AccountPhone string `json:"accountPhone"` // 客户手机号
CustomerIp string `json:"customerIp"` // 客户IP
NotifyUrl string `json:"notifyUrl"` // 回调地址
SuccessPageUrl string `json:"successPageUrl"` // 成功跳转页面
Summary string `json:"summary,omitempty"` // 备注
ReqTime string `json:"reqTime"` // 请求接口时间,13位时间戳
Sign string `json:"sign"` // 签名
}
// WithdrawReq 代付下单请求结构体
type WithdrawReq struct {
MchNo string `json:"mchNo"` // 商户号
MchOrderNo string `json:"mchOrderNo"` // 商户订单号
Currency string `json:"currency"` // 币种
PayAmount string `json:"payAmount"` // 支付金额
Sign string `json:"sign"` // 签名
AccountType string `json:"accountType"` // 收款账户类型
AccountCode string `json:"accountCode"` // 收款账户编码
AccountNo string `json:"accountNo"` // 收款账户号码
AccountName string `json:"accountName"` // 客户姓名
AccountEmail string `json:"accountEmail"` // 客户邮箱
AccountPhone string `json:"accountPhone"` // 客户手机号
CustomerIp string `json:"customerIp"` // 客户IP
NotifyUrl string `json:"notifyUrl"` // 回调地址
Summary string `json:"summary,omitempty"` // 备注
ReqTime string `json:"reqTime"` // 请求时间,13位时间戳
}
// PayWithdrawResponse 代收和代付响应数据体
type PayWithdrawResponse struct {
Code string `json:"code"` // 响应状态码
Message string `json:"message"` // 响应消息
Data OrderData `json:"data,omitempty"` // 响应数据体
Sign string `json:"sign,omitempty"` // 签名
Timestamp string `json:"timestamp"` // 响应时间戳
}
// OrderData 订单数据体
type OrderData struct {
PayOrderNo string `json:"payOrderNo"` // 平台订单号
MchOrderNo string `json:"mchOrderNo"` // 商户订单号
MchNo string `json:"mchNo"` // 商户号
Currency string `json:"currency"` // 币种
PayAmount string `json:"payAmount"` // 金额
PayInitiateTime string `json:"payInitiateTime"` // 下单时间
PayData string `json:"payData"` // 支付链接
PayReference string `json:"payReference"` // 支付二维码
PayState int `json:"payState"` // 支付状态
}
// CallbackReq 代收和代付回调请求结构体
type CallbackReq struct {
PayOrderNo string `json:"payOrderNo"` // 平台订单号
MchOrderNo string `json:"mchOrderNo"` // 商户订单号
MchNo string `json:"mchNo"` // 商户号
Currency string `json:"currency"` // 币种
PayAmount string `json:"payAmount"` // 金额
PayState int `json:"payState"` // 支付状态
PayInitiateTime string `json:"payInitiateTime"` // 下单时间
PayFinishTime string `json:"payFinishTime"` // 完成时间
ErrMsg string `json:"errMsg,omitempty"` // 错误信息
Sign string `json:"sign"` // 签名
}

@ -23,17 +23,17 @@ func NewSub(b *base.Base) {
b.HttpType = base.HttpTypeForm
b.ShouldSignUpper = true
b.Channel = values.PayPlus
if b.Opt == 1 {
if b.Opt == base.OPTPay {
b.Resp = new(PayResp)
b.ReqURL = payApi
} else if b.Opt == 2 {
} else if b.Opt == base.OPTWithdraw {
b.Resp = new(WithdrawResp)
b.ReqURL = withdrawApi
} else if b.Opt == 3 {
} else if b.Opt == base.OPTPayCB {
b.SignPassStr = []string{"sign"}
b.CallbackResp.Msg = strings.ToLower(statusSuccess)
b.CallbackReq = new(PayCallbackReq)
} else if b.Opt == 4 {
} else if b.Opt == base.OPTWithdrawCB {
b.SignPassStr = []string{"sign", "msg"}
b.CallbackResp.Msg = strings.ToLower(statusSuccess)
b.CallbackReq = new(WithdrawCallbackReq)
@ -57,7 +57,7 @@ func (s *Sub) PackReq() interface{} {
}
func (s *Sub) GetResp() (proto.Message, error) {
if s.Base.Opt == 1 {
if s.Base.Opt == base.OPTPay {
resp := s.Base.Resp.(*PayResp)
if resp.RetCode != statusSuccess || resp.PayUrl == "" {
return nil, errors.New("pay fail")
@ -116,16 +116,15 @@ func (s *Sub) PackWithdrawReq() interface{} {
func (s *Sub) CheckSign(_ string) bool {
checkSign := ""
s.Base.CallbackResp.Msg = statusFail
mySign := ""
if s.Base.Opt == 3 {
if s.Base.Opt == base.OPTPayCB {
req := s.Base.CallbackReq.(*PayCallbackReq)
log.Debug("checkSign pay:%+v", *req)
checkSign = req.Sign
s.Base.CallbackResp.OrderID = req.OrderNo
s.Base.CallbackResp.Success = req.Status == 2
mySign = s.Base.SignMD5(req)
} else if s.Base.Opt == 4 {
} else if s.Base.Opt == base.OPTWithdrawCB {
req := s.Base.CallbackReq.(*WithdrawCallbackReq)
log.Debug("checkSign withdraw:%+v", *req)
checkSign = req.Sign

@ -68,6 +68,7 @@ const (
CamelPay
MoonPay2
PayPlus
LuckyinPay
PayAll
)

Loading…
Cancel
Save