Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

增加扫呗支付 #357

Merged
merged 17 commits into from
Oct 13, 2023
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func main() {
* #### [拉卡拉支付](https://github.com/go-pay/gopay/blob/main/doc/lakala.md)
* #### [Paypal支付](https://github.com/go-pay/gopay/blob/main/doc/paypal.md)
* #### [Apple支付校验](https://github.com/go-pay/gopay/blob/main/doc/apple.md)
* #### [扫呗支付](https://github.com/go-pay/gopay/blob/main/doc/saobei.md)

---

Expand Down
23 changes: 23 additions & 0 deletions doc/saobei.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## 扫呗支付 API


- 扫呗支付:[官方文档中心](https://help.lcsw.cn/xrmpic/q6imdiojes7iq5y1/qg52lx)



> 具体API使用介绍,请参考`gopay/saobei/client_test.go`


### 支付2.0接口
> 请参考`gopay/saobei/pay_test.go`,
* 小程序支付接口(暂无账号为测试可用性):`client.MiniPay()`
* 付款码支付 `client.BarcodePay()`
* 支付查询 `client.Query()`
* 退款申请 `client.Refund()`
* 退款订单查询 `client.QueryRefund()`

### 资金接口
> 请参考`gopay/saobei/merchant_test.go`,

### CBK企业钱包分账
> 请参考`gopay/saobei/account_test.go`,
3 changes: 3 additions & 0 deletions saobei/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package saobei

//CBK企业钱包分账
14 changes: 14 additions & 0 deletions saobei/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package saobei

const (
// payPath 小程序支付接口
miniPayPath = "/pay/open/minipay"
// barcodePayPath 付款码支付(扫码支付)
barcodePayPath = "/pay/open/barcodepay"
// queryPath 支付查询
queryPath = "/pay/open/query"
// refundPath 退款申请
refundPath = "/pay/open/refund"
// queryRefundPath 退款订单查询
queryRefundPath = "/pay/open/queryrefund"
)
9 changes: 9 additions & 0 deletions saobei/cert/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cert

const (
Key = "" // 机构秘钥,扫呗分配
AccessToken = "8ee19b194b504b9c89b88a68b4cdf623" // 支付秘钥,扫呗分配
InstNo = "" // 机构号,扫呗分配
MerchantNo = "858104816000177" // 商户号,扫呗分配
TerminalId = "44350591" // 终端号,扫呗分配
)
95 changes: 95 additions & 0 deletions saobei/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package saobei

import (
"context"
"crypto/md5"
"fmt"
"hash"
"sync"

"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xlog"

"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/xhttp"
)

type Client struct {
instNo string //商户系统机构号inst_no
key string // 商户系统令牌
merchantNo string // 支付系统:商户号
terminalId string // 支付系统:商户号终端号
accessToken string // 支付系统: 令牌
isProd bool // 是否正式环境
payVer string //版本号 当前201
serviceId string //接口类型,当前类型015
hc *xhttp.Client
mu sync.Mutex
md5Hash hash.Hash
}

// NewClient 初始化扫呗客户端
// instNo string //商户系统机构号inst_no
// key string // 商户系统令牌
// merchantNo string // 支付系统:商户号
// terminalId string // 支付系统:商户号终端号
// accessToken string // 支付系统: 令牌
// isProd:是否是正式环境
func NewClient(instNo, key, merchantNo, terminalId, accessToken string, isProd bool) (*Client, error) {
return &Client{
instNo: instNo,
key: key,
merchantNo: merchantNo,
terminalId: terminalId,
accessToken: accessToken,
isProd: isProd,
hc: xhttp.NewClient(),
md5Hash: md5.New(),
payVer: "201",
serviceId: "015",
}, nil
}

// pubParamsHandle 公共参数处理
func (c *Client) pubParamsHandle(bm gopay.BodyMap) gopay.BodyMap {
if ver := bm.GetString("pay_ver"); ver == util.NULL {
bm.Set("pay_ver", c.payVer)
}
if v := bm.GetString("service_id"); v == util.NULL {
bm.Set("service_id", c.serviceId)
}
if v := bm.GetString("merchant_no"); v == util.NULL {
bm.Set("merchant_no", c.merchantNo)
}
if v := bm.GetString("terminal_id"); v == util.NULL {
bm.Set("terminal_id", c.terminalId)
}
sign := c.getRsaSign(bm)

bm.Set("key_sign", sign)

return bm
}

// doPost 发起请求
func (c *Client) doPost(ctx context.Context, path string, bm gopay.BodyMap) (bs []byte, err error) {
param := c.pubParamsHandle(bm)
if err != nil {
return nil, err
}

xlog.Debugf("saobeiParam:%+v", param.JsonBody())

url := baseUrl
if !c.isProd {
url = sandboxBaseUrl
}
res, bs, err := c.hc.Req(xhttp.TypeJSON).Post(url + path).SendBodyMap(param).EndBytes(ctx)
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
}
return bs, nil
}
33 changes: 33 additions & 0 deletions saobei/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package saobei

import (
"context"
"os"
"testing"

"github.com/go-pay/gopay/pkg/xlog"
"github.com/go-pay/gopay/saobei/cert"
)

var (
ctx = context.Background()
client *Client
err error
)

func TestMain(m *testing.M) {
// 初始化通联客户端
// instNo string //商户系统机构号inst_no
// key string // 商户系统令牌
// merchantNo string // 支付系统:商户号
// terminalId string // 支付系统:商户号终端号
// accessToken string // 支付系统: 令牌
// isProd:是否是正式环境
client, err = NewClient(cert.InstNo, cert.Key, cert.MerchantNo, cert.TerminalId, cert.AccessToken, false)
if err != nil {
xlog.Error(err)
return
}

os.Exit(m.Run())
}
41 changes: 41 additions & 0 deletions saobei/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package saobei

const (
baseUrl = "https://pay.lcsw.cn/lcsw"
sandboxBaseUrl = "http://test2.lcsw.cn:8117/lcsw"

//PayTypeWX 支付方式:微信
PayTypeWX = "010"
//PayTypeAli 支付方式:支付宝
PayTypeAli = "020"
//PayTypeQQ 支付方式:QQ钱包
PayTypeQQ = "060"
//PayTypeYi 支付方式:翼支付
PayTypeYi = "100"
//PayTypeYL 支付方式:银联二维码
PayTypeYL = "110"

//ResultCodeSuccess 业务结果:01 成功
ResultCodeSuccess = "01"
//ResultCodeFail 业务结果:02 失败
ResultCodeFail = "02"
//ResultCodePaying 业务结果:03 支付中
ResultCodePaying = "03"

//TradeStatusSuccess 交易订单状态:支付成功
TradeStatusSuccess = "SUCCESS"
//TradeStatusRefund 交易订单状态:转入退款
TradeStatusRefund = "REFUND"
//TradeStatusNotPay 交易订单状态:未支付
TradeStatusNotPay = "NOTPAY"
//TradeStatusClosed 交易订单状态:已关闭
TradeStatusClosed = "CLOSED"
//TradeStatusUserPaying 交易订单状态:用户支付中
TradeStatusUserPaying = "USERPAYING"
//TradeStatusRevoked 交易订单状态:已撤销
TradeStatusRevoked = "REVOKED"
//TradeStatusNoPay 交易订单状态:未支付支付超时
TradeStatusNoPay = "NOPAY"
//TradeStatusPayError 交易订单状态:支付失败
TradeStatusPayError = "PAYERROR"
)
32 changes: 32 additions & 0 deletions saobei/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package saobei

import (
"fmt"
)

// BizErr 用于判断通联的业务逻辑是否有错误
type BizErr struct {
Code string `json:"code"`
Msg string `json:"msg"`
}

// bizErrCheck 检查返回码是否为SUCCESS 否则返回一个BizErr
func bizErrCheck(resp RspBase) error {
if resp.ReturnCode != "01" {
return &BizErr{
Code: resp.ReturnCode,
Msg: resp.ReturnMsg,
}
}
//if resp.ResultCode != "01" {
// return &BizErr{
// Code: resp.ResultCode,
// Msg: resp.ReturnMsg,
// }
//}
return nil
}

func (e *BizErr) Error() string {
return fmt.Sprintf(`[%s]%s`, e.Code, e.Msg)
}
1 change: 1 addition & 0 deletions saobei/merchant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package saobei
Loading