diff options
| -rw-r--r-- | client.go | 33 | ||||
| -rw-r--r-- | client_test.go | 48 | ||||
| -rw-r--r-- | duitku_test.go | 28 | ||||
| -rw-r--r-- | payment_fee.go | 25 | ||||
| -rw-r--r-- | payment_method.go | 64 | ||||
| -rw-r--r-- | payment_method_response.go | 11 | ||||
| -rw-r--r-- | testdata/merchant/payment_method_test.txt | 106 |
7 files changed, 314 insertions, 1 deletions
@@ -35,6 +35,8 @@ const ( PathTransferClearing = `/webapi/api/disbursement/transferclearing` PathTransferClearingSandbox = `/webapi/api/disbursement/transferclearingsandbox` // Used for testing. + + PathMerchantPaymentMethod = `/webapi/api/merchant/paymentmethod/getpaymentmethod` ) type Client struct { @@ -256,6 +258,37 @@ func (cl *Client) ListBank() (banks []Bank, err error) { return banks, nil } +// MerchantPaymentMethod get active payment methods from the merchant (your) +// project. +// +// Ref: https://docs.duitku.com/api/en/#get-payment-method +func (cl *Client) MerchantPaymentMethod(req *PaymentMethod) (resp *PaymentMethodResponse, err error) { + var ( + logp = `MerchantPaymentMethod` + path = PathMerchantPaymentMethod + + resHttp *http.Response + resBody []byte + ) + + req.Sign(cl.opts) + + resHttp, resBody, err = cl.PostJSON(path, nil, req) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + if resHttp.StatusCode >= 500 { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + + err = json.Unmarshal(resBody, &resp) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + + return resp, nil +} + // Options return the current client configuration. func (cl *Client) Options() (opts ClientOptions) { return cl.opts diff --git a/client_test.go b/client_test.go index 67865ae..a4c9a1e 100644 --- a/client_test.go +++ b/client_test.go @@ -199,6 +199,54 @@ func TestClient_InquiryStatus_sandbox(t *testing.T) { test.Assert(t, `InquiryStatus`, expInquiryStatus, resInqueryStatus) } +func TestClient_MerchantPaymentMethod(t *testing.T) { + var ( + tdata *test.Data + err error + ) + + err = initClientMerchant() + if err != nil { + t.Skip(err) + } + + tdata, err = test.LoadData(`testdata/merchant/payment_method_test.txt`) + if err != nil { + t.Fatal(err) + } + + var ( + req *PaymentMethod + resp *PaymentMethodResponse + tag string + ) + + tag = `payment_method_request.json` + err = json.Unmarshal(tdata.Input[tag], &req) + if err != nil { + t.Fatal(err) + } + + resp, err = testClientMerchant.MerchantPaymentMethod(req) + if err != nil { + t.Fatal(err) + } + + var ( + exp []byte + got []byte + ) + + got, err = json.MarshalIndent(resp, ``, ` `) + if err != nil { + t.Fatal(err) + } + + tag = `payment_method_response.json` + exp = tdata.Output[tag] + test.Assert(t, `PaymentMethod`, string(exp), string(got)) +} + func TestClient_RtolInquiry_sandbox(t *testing.T) { var ( err error diff --git a/duitku_test.go b/duitku_test.go index 9f158c5..f92d884 100644 --- a/duitku_test.go +++ b/duitku_test.go @@ -4,13 +4,15 @@ package duitku import ( + "fmt" "log" "os" "testing" ) var ( - testClient *Client + testClient *Client + testClientMerchant *Client ) func TestMain(m *testing.M) { @@ -31,3 +33,27 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } + +func initClientMerchant() (err error) { + if testClientMerchant != nil { + return nil + } + + var ( + logp = `initClientMerchant` + + opts *ClientOptions + ) + + opts, err = LoadClientOptions(`client.conf`) + if err != nil { + return fmt.Errorf(`%s: %w`, logp, err) + } + + testClientMerchant, err = NewClient(*opts) + if err != nil { + return fmt.Errorf(`%s: %w`, logp, err) + } + + return nil +} diff --git a/payment_fee.go b/payment_fee.go new file mode 100644 index 0000000..4b8f10f --- /dev/null +++ b/payment_fee.go @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later + +package duitku + +// [PaymentFee] contains fee for payment method. +// +// If the settings in the merchant portal fees are charged to the +// merchant, the TotalFee will appear 0. +// The TotalFee will appear if it is charged to the customer. +// +// [PaymentFee]: https://docs.duitku.com/api/en/#payment-fee +type PaymentFee struct { + // Payment method code. + PaymentMethod string `json:"paymentMethod"` + + // Payment method name. + PaymentName string `json:"paymentName"` + + // Payment method image url. + PaymentImage string `json:"paymentImage"` + + // Payment Fee. + TotalFee string `json:"totalFee"` +} diff --git a/payment_method.go b/payment_method.go new file mode 100644 index 0000000..b5e95fa --- /dev/null +++ b/payment_method.go @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2023 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later + +package duitku + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "time" +) + +const ( + // PaymentMethodDatetimeLayout define the date and time format for + // PaymentMethod.DateTime. + PaymentMethodDatetimeLayout = time.DateTime +) + +// PaymentMethod contains request for client MerchantPaymentMethod. +type PaymentMethod struct { + // [REQ] Merchant code from Duitku. + // Set by calling sign from ClientOptions. + MerchantCode string `json:"merchantcode"` + + // [REQ] Format: yyyy-MM-dd HH:mm:ss + // If empty, it will be set during sign using current date time. + DateTime string `json:"datetime"` + + // [REQ] Formula: Sha256(merchantcode + paymentAmount + datetime + + // apiKey). + // The value of hash is stored as lowercase hexadecimal. + Signature string `json:"signature"` + + // [REQ] Transaction amount. No decimal code (.) and no decimal digit. + Amount int64 `json:"amount"` +} + +// SetDateTime set the field DateTime using t. +func (req *PaymentMethod) SetDateTime(t time.Time) { + req.DateTime = t.Format(PaymentMethodDatetimeLayout) +} + +// Sign the request. +// Set the MerchantCode and DateTime if its empty, and then generate the +// Signature. +func (req *PaymentMethod) Sign(opts ClientOptions) { + var ( + plain string + hash [sha256.Size]byte + ) + + if len(req.MerchantCode) == 0 { + req.MerchantCode = opts.MerchantCode + } + + if len(req.DateTime) == 0 { + req.SetDateTime(time.Now()) + } + + plain = fmt.Sprintf(`%s%d%s%s`, req.MerchantCode, req.Amount, req.DateTime, opts.MerchantApiKey) + hash = sha256.Sum256([]byte(plain)) + + req.Signature = hex.EncodeToString(hash[:]) +} diff --git a/payment_method_response.go b/payment_method_response.go new file mode 100644 index 0000000..4d901d8 --- /dev/null +++ b/payment_method_response.go @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later + +package duitku + +// PaymentMethodResponse contains list of payments enabled by merchant. +type PaymentMethodResponse struct { + Response + + PaymentFee []PaymentFee `json:"paymentFee"` +} diff --git a/testdata/merchant/payment_method_test.txt b/testdata/merchant/payment_method_test.txt new file mode 100644 index 0000000..9ecca1e --- /dev/null +++ b/testdata/merchant/payment_method_test.txt @@ -0,0 +1,106 @@ +>>> payment_method_request.json +{} + +<<< payment_method_response.json +{ + "responseCode": "00", + "responseDesc": "", + "paymentFee": [ + { + "paymentMethod": "VA", + "paymentName": "MAYBANK VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/VA.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "BT", + "paymentName": "PERMATA VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/PERMATA.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "B1", + "paymentName": "CIMB NIAGA VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/B1.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "A1", + "paymentName": "ATM BERSAMA VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/A1.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "OV", + "paymentName": "OVO", + "paymentImage": "https://images.duitku.com/hotlink-ok/OV.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "F5", + "paymentName": "MANDIRI VA DEPOSIT", + "paymentImage": "https://images.duitku.com/hotlink-ok/", + "totalFee": "0" + }, + { + "paymentMethod": "M1", + "paymentName": "MANDIRI VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/MV.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "M2", + "paymentName": "MANDIRI VA H2H", + "paymentImage": "https://images.duitku.com/hotlink-ok/MV.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "AG", + "paymentName": "ARTHA GRAHA VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/AG.JPG", + "totalFee": "0" + }, + { + "paymentMethod": "S1", + "paymentName": "SAMPOERNA VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/S1.JPG", + "totalFee": "0" + }, + { + "paymentMethod": "SP", + "paymentName": "SHOPEEPAY QRIS", + "paymentImage": "https://images.duitku.com/hotlink-ok/SHOPEEPAY.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "SA", + "paymentName": "SHOPEEPAY APP", + "paymentImage": "https://images.duitku.com/hotlink-ok/SHOPEEPAY.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "SL", + "paymentName": "SHOPEEPAY LINK", + "paymentImage": "https://images.duitku.com/hotlink-ok/SHOPEEPAY.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "BR", + "paymentName": "BRI VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/BR.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "NC", + "paymentName": "BNC VA", + "paymentImage": "https://images.duitku.com/hotlink-ok/NC.PNG", + "totalFee": "0" + }, + { + "paymentMethod": "IR", + "paymentName": "INDOMARET", + "paymentImage": "https://images.duitku.com/hotlink-ok/IR.PNG", + "totalFee": "0" + } + ] +} |
