alipay

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2021 License: MIT Imports: 4 Imported by: 0

README

Better Aliapy Go

Go Report Card GoDoc

A better Alipay SDK for Golang. With First-class function, without the tears 😢 GoDoc

  • Thread safe
  • Hooks friendly
  • Tracing friendly
  • Dynamic configuration friendly
  • Multiple configuration friendly

一个更好的支付宝(Alipay)SDK。函数优先,没有眼泪😢 中文文档

  • 多线程安全
  • Hooks
  • 链路跟踪
  • 动态配置
  • 多个配置

Contents

Installation

To install Better-Alipay-Go package, you need to install Go and set your Go workspace first.

  1. The first need Go installed, then you can use the below Go command to install it.
$ go get -u github.com/WenyXu/better-alipay-go
  1. Import it in your code:
import "github.com/WenyXu/better-alipay-go"

Quick Start

package main
import (
    alipay "github.com/WenyXu/better-alipay-go"
    "github.com/WenyXu/better-alipay-go/options"
    "github.com/WenyXu/better-alipay-go/global"
    "github.com/WenyXu/better-alipay-go/m"
    "os"
)
func main(){
	// init a default client with a app configuration
    s := alipay.Default(
        options.AppId(os.Getenv("APP_ID")),
        options.PrivateKey(os.Getenv("PrivateKey")),
        // Depended on you AppCert type 
        // if you'd like load the cert form []byte
        // just use:
        // options.AppCertBytes()
        // if you already save cert sn at somewhere
        // just use:
        // options.AppCertSn()
        options.AppCertPath("./cert_file/appCertPublicKey.crt"),
        // similar to the AppCertPath
        // also provide:
        // options.RootCertBytes()
        // options.RootCertSn()
        options.RootCertPath("./cert_file/alipayRootCert.crt"),
        // similar to the AppCertPath
        // also provide:
        // options.PublicCertBytes()
        // options.PublicCertSn()
        options.PublicCertPath("./cert_file/alipayCertPublicKey_RSA2.crt"),
        options.Production(false),
        // or global.PKCS1
        options.PrivateKeyType(global.PKCS8),
        // or global.RSA
        options.SignType(global.RSA2),
    )
	
    // of course, you can using a struct instead
    // var resp AlipayTradeCreateResponse
    resp := make(map[string]interface{})
    _ = s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
          param.
              Set("subject", "网站测试支付").
              Set("buyer_id", "2088802095984694").
              Set("out_trade_no", "123456786543").
              Set("total_amount", "88.88"),
    }), &resp)

    // without biz-content request
    _ = s.Request(global.AlipaySystemOauthToken, m.NewMap(func(param m.M) {
        // set key value as public params 
    	param.
        	Set("grant_type", "authorization_code").
        	Set("code", "3a06216ac8f84b8c93507bb9774bWX11")
    }),
        &resp,
        options.SetMakeReqFunc(options.WithoutBizContentMakeReqFunc),
    )
	
}
    

Global Configuration

You can use following functions to configure global configuration.

package main

import (
	"github.com/WenyXu/better-alipay-go/options"
	"time"
)

func main() {
	// set Default Transport 
	loc, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		panic(err)
	}
	options.SetDefaultLocation(options.SetLocation(loc))
	
	// set Default Transport which implement http.RoundTripper interface
	transport := YourCustomTransport()
	options.SetDefaultTransport(transport)

	// set Default MakeRequestFunc which implement options.MakeRequestFunc func
	options.SetDefaultMakeReqFunc(yourMakeReqFunc)

	// set Default DecFunc which implement options.DecFunc func
	options.SetDefaultDecFunc(yourDecFunc)
	
	// set Default Logger which implement logger.Logger interface
	// built-in :
	// logger.NullLogger
	// logger.StdLogger
	options.SetDefaultLogger(yourLogger)
}

After above configuring, alipay.New / alipay.Default / options.newOptions will return new Options with your configured.

// options.go 
func newOptions(opts ...Option) Options {
	opt := Options{
		Transport: DefaultTransport,
		Context:   context.Background(),
		MakeReq:   DefaultMakeReqFunc,
		Dec:       DefaultDecFunc,
		Logger:    DefaultLogger,
	}
	for _, o := range opts {
		o(&opt)
	}
	return opt
}

Dynamic Configuration

If your application have multiple configurations, you can configure configuration dynamically.

When you call the Request func, it will make a copy form your current Options, then modify the Options. generally, it will be thread-safe.

Basic Usage

Configure per request

package main
import (
    alipay "github.com/WenyXu/better-alipay-go"
    "github.com/WenyXu/better-alipay-go/options"
    "github.com/WenyXu/better-alipay-go/global"
    "github.com/WenyXu/better-alipay-go/m"
    "os"
)
func main(){
	// init a default client with a app configuration
    s := alipay.Default(
        options.AppId(os.Getenv("APP_ID")),
        options.PrivateKey(os.Getenv("PrivateKey")),
        options.AppCertPath("./cert_file/appCertPublicKey.crt"),
        options.RootCertPath("./cert_file/alipayRootCert.crt"),
        options.PublicCertPath("./cert_file/alipayCertPublicKey_RSA2.crt"),
        options.Production(false),
        options.PrivateKeyType(global.PKCS8),
        options.SignType(global.RSA2),
    )
	
    // of course, you can using a struct instead
    // var resp AlipayTradeCreateResponse
    resp := make(map[string]interface{})
	s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
            param.
                Set("subject", "网站测试支付").
                Set("buyer_id", "2088802095984694").
                Set("out_trade_no", "123456786543").
                Set("total_amount", "88.88")
        }),
        &resp,
        // dynamic configuration
        options.AppAuthToken("your-app-auth-token"),
        options.AuthToken("your-auth-token"),
	)
}
Advanced Usage

Configure with func which implement options.Option.

    // implement options.Option
    // type Option func(*Options)
    func CustomOption(o *options.Options) {
        // modify your app config 
        // using 
        o.Config.AppId="whatever"
    }
    
    ...

    func main(){
        // use custom option in New or Default func 
		s := alipay.Default(
			...,
        )
        // or use custom option in Request of MakeParam func 
        s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
              param.
              Set("subject", "网站测试支付").
              Set("buyer_id", "2088802095984694").
              Set("out_trade_no", "123456786543").
              Set("total_amount", "88.88")
          }),
          &resp,
          // your custom option
          CustomOption,
        )
    }   
    ...
    
    

Use a client without default app configuration and configure per request.

package main
import (
    alipay "github.com/WenyXu/better-alipay-go"
    "github.com/WenyXu/better-alipay-go/options"
    "github.com/WenyXu/better-alipay-go/global"
    "github.com/WenyXu/better-alipay-go/m"
)

func main(){
	// init a empty client 
    s := alipay.Default()
    
    // of course, you can using a struct instead
    // var resp AlipayTradeCreateResponse
    resp := make(map[string]interface{})
	s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
		param.
			Set("subject", "网站测试支付").
			Set("buyer_id", "2088802095984694").
			Set("out_trade_no", "123456786543").
			Set("total_amount", "88.88")
	    }),
	    &resp,
	    // dynamic configuration pre Request func
	    options.AppId(os.Getenv("APP_ID")),
	    options.PrivateKey(os.Getenv("PrivateKey")),
	    options.AppCertPath("./cert_file/appCertPublicKey.crt"),
            options.RootCertPath("./cert_file/alipayRootCert.crt"),
            options.PublicCertPath("./cert_file/alipayCertPublicKey_RSA2.crt"),
            options.Production(false),
            options.PrivateKeyType(global.PKCS8),
            options.SignType(global.RSA2),
	)
}

Load configuration form database or somewhere, and configure dynamically at per request.

package main
import (
	alipay "github.com/WenyXu/better-alipay-go"
	"github.com/WenyXu/better-alipay-go/options"
	"github.com/WenyXu/better-alipay-go/m"
)

func MakeAppConfig(yourConfig ConfigEntity) options.Option {
	return func(o *options.Options) {
		// modify your app config 
		// using 
		o.Config.AppId=yourConfig.AppId
		...
	}
}
func main()  {
	// init a empty client 
	s := alipay.Default()

	// you config entity
	config:=ReadFormSomeWhere()
    
	// of course, you can using a struct instead
	// var resp AlipayTradeCreateResponse
	resp := make(map[string]interface{})
	s.Request("alipay.trade.create",m.NewMap(func(param m.M) {
              param.
                  Set("subject", "网站测试支付").
                  Set("buyer_id", "2088802095984694").
                  Set("out_trade_no", "123456786543").
                  Set("total_amount", "88.88")
	    }),
        &resp,
        // dynamic configuration pre Request func
        MakeAppConfig(config),
	)
    
}

Hooks

In Options, we provide hooks which run before request started, and after response received, so you can do something like inject context, tracing's span etc. it is just similar to web middleware. We provide a sample here, help you have a concept.

// options.go
// DefaultBeforeFunc log the request body
func DefaultBeforeFunc(ctx context.Context, req *http.Request) context.Context {
    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        fmt.Printf("Read Request body with error: %s", err.Error())
        return ctx
    }
    req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
    fmt.Println(string(body))
    return ctx
}

// DefaultAfterFunc log the response body
func DefaultAfterFunc(ctx context.Context, resp *http.Response) context.Context {
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Read Response body with error: %s", err.Error())
        return ctx
    }
    resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
    fmt.Println(string(body))
    return ctx
}

The Request fun will run before-hooks before the request started, and run after-hook after response received.

//alipay.go
// Do Request
func (s service) Request(method string, param m.M, response interface{}, opts ...options.Option) (err error) {
    copyOpts := s.opts.Copy()
    
    ...
    
    // run before hooks before request started
    for _, f := range copyOpts.Before {
        ctx = f(ctx, req)
    }
    
    ...
    
    // do request
    resp, err := copyOpts.Transport.RoundTrip(req)
    
    
    ...
    
    // run after hooks after response received
    for _, f := range copyOpts.After {
        ctx = f(ctx, resp)
    }
    
    ...
}

Tracing

You can inject context, when your call a Request func, and use hook to finish the tracing.

package main

import (
	"context"
	alipay "github.com/WenyXu/better-alipay-go"
	"github.com/WenyXu/better-alipay-go/options"
	"github.com/WenyXu/better-alipay-go/m"
	"github.com/opentracing/opentracing-go"
	"net/http"
)

func main() {
	s := alipay.Default()
	// get trace instance
	trace := yourTracingInstance()
	resp := make(map[string]interface{})
	s.Request("alipay.trade.create", m.NewMap(func(param m.M) {
		param.
			Set("subject", "网站测试支付").
			Set("buyer_id", "2088802095984694").
			Set("out_trade_no", "123456786543").
			Set("total_amount", "88.88")
	}),
	&resp,
	// inject your tracing context
	func(trace opentracing.Tracer) options.Option {
		return func(o *options.Options) {
			sp := opentracing.StartSpan("tarcing name")
			// injected
			o.Context = context.WithValue(o.Context,"span-key",sp)
		}
	}(trace),
	// handle span finish
	options.AppendAfterFunc(func(c context.Context, response *http.Response) context.Context {
            sp, ok := c.Get("span-key")
            if ok {
                // span finish
                sp.Finish()
            }
		    return c
	    }),
	)

}

Built-in Methods constant and Response structs

Following methods:

        // https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.enterinfo.sync
	AlipayEcoMyCarParkingEnterInfoSync = "alipay.eco.mycar.parking.enterinfo.sync"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.exitinfo.sync
	AlipayEcoMyCarParkingExitInfoSync = "alipay.eco.mycar.parking.exitinfo.sync"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.vehicle.query
	AlipayEcoMyCarParkingVehicleQuery = "alipay.eco.mycar.parking.vehicle.query"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.order.sync
	AlipayEcoMyCarParkingOrderSync = "alipay.eco.mycar.parking.order.sync"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.order.update
	AlipayEcoMyCarParkingOrderUpdate = "alipay.eco.mycar.parking.order.update"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.config.set
	AlipayEcoMyCarParkingConfigSet = "alipay.eco.mycar.parking.config.set"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.parkinglotinfo.update
	AlipayEcoMyCarParkingParkingLotInfoUpdate = "alipay.eco.mycar.parking.parkinglotinfo.update"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.parkinglotinfo.create
	AlipayEcoMyCarParkingParkingLotInfoCreate = "alipay.eco.mycar.parking.parkinglotinfo.create"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.parkinglotinfo.query
	AlipayEcoMyCarParkingParkingLotInfoQuery = "alipay.eco.mycar.parking.parkinglotinfo.query"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.order.pay
	AlipayEcoMyCarParkingOrderPay = "alipay.eco.mycar.parking.order.pay"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.trade.order.query
	AlipayEcoMyCarTradeOrderQuery = "alipay.eco.mycar.trade.order.query"

	// https://opendocs.alipay.com/apis/api_19/alipay.eco.mycar.parking.agreement.query
	AlipayEcoMyCarParkingAgreement = "alipay.eco.mycar.parking.agreement.query"

	// https://opendocs.alipay.com/apis/api_9/alipay.user.info.auth
	AlipayUserInfoAuth = "alipay.user.info.auth"

	// https://opendocs.alipay.com/apis/api_9/alipay.system.oauth.token
	AlipaySystemOauthToken = "alipay.system.oauth.token"

	// https://opendocs.alipay.com/apis/api_9/alipay.open.auth.token.app
	AlipayOpenAuthTokenApp = "alipay.open.auth.token.app"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay
	AlipayTradePagePay = "alipay.trade.page.pay"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay
	AlipayTradeAppPay = "alipay.trade.app.pay"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay
	AlipayTradeWapPay = "alipay.trade.wap.pay"

	// https://opendocs.alipay.com/apis/api_2/alipay.user.certify.open.certify
	AlipayUserCertifyOpenCertify = "alipay.user.certify.open.certify"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.fastpay.refund.query
	AlipayTradeFastpayRefundQuery = "alipay.trade.fastpay.refund.query"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.order.settle
	AlipayTradeOrderSettle = "alipay.trade.order.settle"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.create
	AlipayTradeCreate = "alipay.trade.create"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.close
	AlipayTradeClose = "alipay.trade.close"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.cancel
	AlipayTradeCancel = "alipay.trade.cancel"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.refund
	AlipayTradeRefund = "alipay.trade.refund"

	// https://opendocs.alipay.com/apis/api_1/alipay.page.trade.refund
	AlipayTradePageRefund = "alipay.trade.page.refund"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate
	AlipayTradePrecreate = "alipay.trade.precreate"

	// https://opendocs.alipay.com/apis/api_1/alipay.trade.query
	AlipayTradeQuery = "alipay.trade.query"

	// https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.toaccount.transfer
	AlipayFundTransToAccountTransfer = "alipay.fund.trans.toaccount.transfer"

	// https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer
	AlipayFundTransUniTransfer = "alipay.fund.trans.uni.transfer"

	// https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.common.query
	AlipayFundTransCommonQuery = "alipay.fund.trans.common.query"

	// https://opendocs.alipay.com/apis/api_28/alipay.fund.account.query
	AlipayFundAccountQuery = "alipay.fund.account.query"

	// https://opendocs.alipay.com/apis/api_2/alipay.user.info.share
	AlipayUserInfoShare = "alipay.user.info.share"

	// https://opendocs.alipay.com/apis/api_8/zhima.credit.score.get
	ZhimaCreditScoreGet = "zhima.credit.score.get"

	// https://opendocs.alipay.com/apis/api_2/alipay.user.certify.open.initialize
	AlipayUserCertifyOpenInitialize = "alipay.user.certify.open.initialize"

	// https://opendocs.alipay.com/apis/api_2/alipay.user.certify.open.query
	AlipayUserCertifyOpenQuery = "alipay.user.certify.open.query"

You can use Success() func of these structs to check if the response return successfully

    ...
    var resp AlipayTradeCreate
    ...
    if resp.Success() {
    	....
    }   

Inspirited

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Service

type Service interface {
	Options() options.Options
	Request(method string, param m.M, response interface{}, opts ...options.Option) (err error)
	MakeParam(method string, param m.M, opts ...options.Option) (data m.M, err error)
}

func Default

func Default(opts ...options.Option) Service

Default return a default service

s := Default()
// same as
s := New(
	options.DefaultLocation,
	options.DefaultCharset(),
	options.DefaultFormat(),
	options.DefaultVersion(),
)
Example (Advanced)
s := Default()

// of course, you can using a struct instead
// var resp AlipayTradeCreateResponse
resp := make(map[string]interface{})
_ = s.Request("alipay.trade.create", m.NewMap(func(param m.M) {
	param.
		Set("subject", "网站测试支付").
		Set("buyer_id", "2088802095984694").
		Set("out_trade_no", "123456786543").
		Set("total_amount", "88.88")
}),
	&resp,
	// dynamic configuration pre Request func
	options.AppId(os.Getenv("APP_ID")),
	options.PrivateKey(os.Getenv("PrivateKey")),
	options.AppCertPath("./cert_file/appCertPublicKey.crt"),
	options.RootCertPath("./cert_file/alipayRootCert.crt"),
	options.PublicCertPath("./cert_file/alipayCertPublicKey_RSA2.crt"),
	options.Production(false),
	options.PrivateKeyType(global.PKCS8),
	options.SignType(global.RSA2),
)
Output:

Example (Advanced_2)
s := Default()

// of course, you can using a struct instead
// var resp AlipayTradeCreateResponse
resp := make(map[string]interface{})
_ = s.Request("alipay.trade.create", m.NewMap(func(param m.M) {
	param.
		Set("subject", "网站测试支付").
		Set("buyer_id", "2088802095984694").
		Set("out_trade_no", "123456786543").
		Set("total_amount", "88.88")
}),
	&resp,
	// dynamic configuration pre Request func
	MakeAppConfig(struct{ AppId string }{AppId: ""}),
)
Output:

Example (Basic)
// init a default client with a app configuration
_ = Default(
	options.AppId(os.Getenv("APP_ID")),
	options.PrivateKey(os.Getenv("PrivateKey")),
	// Depended on you AppCert type
	// if you'd like load the cert form []byte
	// just use:
	// options.AppCertBytes()
	// if you already save cert sn at somewhere
	// just use:
	// options.AppCertSn()
	options.AppCertPath("./cert_file/appCertPublicKey.crt"),
	// similar to the AppCertPath
	// also provide:
	// options.RootCertBytes()
	// options.RootCertSn()
	options.RootCertPath("./cert_file/alipayRootCert.crt"),
	// similar to the AppCertPath
	// also provide:
	// options.PublicCertBytes()
	// options.PublicCertSn()
	options.PublicCertPath("./cert_file/alipayCertPublicKey_RSA2.crt"),
	options.Production(false),
	// or global.PKCS1
	options.PrivateKeyType(global.PKCS8),
	// or global.RSA
	options.SignType(global.RSA2),
)
Output:

func New

func New(opts ...options.Option) Service

New will return a empty service, using global default configurations

you can use:

options.SetDefaultTransport(f http.RoundTripper)

options.SetDefaultDecFunc(f DecFunc)

options.SetDefaultMakeReqFunc(f MakeReqFunc)

options.SetDefaultLogger(f logger.Logger)

options.SetDefaultLocation(f Option)

To modify default global configurations.

opt := Options{
	Transport: DefaultTransport,
	Context:   context.Background(),
	MakeReq:   DefaultMakeReqFunc,
	Dec:       DefaultDecFunc,
	Logger:    DefaultLogger,
}

Directories

Path Synopsis
Utility for key and cert file
Utility for key and cert file
Module for configure the sdk configurations
Module for configure the sdk configurations
Module for handle errors
Module for handle errors
Global constants
Global constants
Module for logger
Module for logger
m
Utility for map[string]interface{}
Utility for map[string]interface{}
Utility for handle notify request (*http.Request or url.Values)
Utility for handle notify request (*http.Request or url.Values)
Module for setup AppConfig, ClientConfig
Module for setup AppConfig, ClientConfig
Utility for sign the param and validate sign & data
Utility for sign the param and validate sign & data

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL