diff options
| author | Shulhan <ms@kilabit.info> | 2023-02-01 14:56:51 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2023-02-01 15:26:41 +0700 |
| commit | 78469f295acd5931d64c8af483c0261ff2eb6183 (patch) | |
| tree | ff055c4b6759ff4e40f8bc3175de1968cb21e8a5 | |
| parent | dd24a0248dc011fe43540b7a8f1487ed7d1e5d90 (diff) | |
| download | duitku-78469f295acd5931d64c8af483c0261ff2eb6183.tar.xz | |
all: implement API to check merchant transaction status
The MerchantTxStatus method check the payment status by its orderID and
payment method.
Reference: https://docs.duitku.com/api/en/#check-transaction
| -rw-r--r-- | client.go | 44 | ||||
| -rw-r--r-- | client_test.go | 22 | ||||
| -rw-r--r-- | testdata/merchant/inquiry_test.txt | 9 | ||||
| -rw-r--r-- | transaction_status.go | 32 | ||||
| -rw-r--r-- | transaction_status_response.go | 28 |
5 files changed, 133 insertions, 2 deletions
@@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" "sort" "strings" @@ -36,8 +37,9 @@ const ( PathTransferClearing = `/webapi/api/disbursement/transferclearing` PathTransferClearingSandbox = `/webapi/api/disbursement/transferclearingsandbox` // Used for testing. - PathMerchantPaymentMethod = `/webapi/api/merchant/paymentmethod/getpaymentmethod` - PathMerchantInquiry = `/webapi/api/merchant/v2/inquiry` + PathMerchantInquiry = `/webapi/api/merchant/v2/inquiry` + PathMerchantPaymentMethod = `/webapi/api/merchant/paymentmethod/getpaymentmethod` + PathMerchantTransactionStatus = `/webapi/api/merchant/transactionStatus` ) type Client struct { @@ -323,6 +325,44 @@ func (cl *Client) MerchantPaymentMethod(req *PaymentMethod) (resp *PaymentMethod return resp, nil } +// [MerchantTxStatus] get the status of payment from customer. +// +// [MerchantTxStatus]: https://docs.duitku.com/api/en/#check-transaction +func (cl *Client) MerchantTxStatus(orderID, paymentMethod string) (resp *TxStatusResponse, err error) { + var ( + logp = `MerchantTxStatus` + req = transactionStatus{ + OrderID: orderID, + } + + params url.Values + httpRes *http.Response + resBody []byte + ) + + req.sign(cl.opts, paymentMethod) + + params, err = libhttp.MarshalForm(req) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + + httpRes, resBody, err = cl.PostForm(PathMerchantTransactionStatus, nil, params) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + if httpRes.StatusCode >= 400 { + return nil, fmt.Errorf(`%s: %s: %s`, logp, httpRes.Status, resBody) + } + + 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 832bf3a..2c58db5 100644 --- a/client_test.go +++ b/client_test.go @@ -254,6 +254,28 @@ func TestClient_MerchantInquiry(t *testing.T) { t.Logf(`MerchantInquiry: response: %s`, got) test.Assert(t, `MerchantInquiry`, string(exp), string(got)) + + // Test checking the transaction status. + + var ( + txResp *TxStatusResponse + ) + + txResp, err = testClientMerchant.MerchantTxStatus(req.MerchantOrderId, req.PaymentMethod) + if err != nil { + t.Fatal(err) + } + + got, err = json.MarshalIndent(txResp, ``, ` `) + if err != nil { + t.Fatal(err) + } + + tag = `tx_status_response.json` + exp = tdata.Output[tag] + exp = bytes.ReplaceAll(exp, []byte(`$ref`), []byte(resp.Reference)) + t.Logf(`MerchantTxStatus: response: %s`, got) + test.Assert(t, `MerchantTxStatus`, string(exp), string(got)) } func TestClient_MerchantPaymentMethod(t *testing.T) { diff --git a/testdata/merchant/inquiry_test.txt b/testdata/merchant/inquiry_test.txt index c597b22..05262d7 100644 --- a/testdata/merchant/inquiry_test.txt +++ b/testdata/merchant/inquiry_test.txt @@ -19,3 +19,12 @@ "qrString": "", "amount": "10000" } + +<<< tx_status_response.json +{ + "merchantOrderId": "1", + "reference": "$ref", + "amount": "10000", + "statusCode": "01", + "statusMessage": "PROCESS" +} diff --git a/transaction_status.go b/transaction_status.go new file mode 100644 index 0000000..7ddae30 --- /dev/null +++ b/transaction_status.go @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later + +package duitku + +import ( + "crypto/md5" + "encoding/hex" + "fmt" +) + +type transactionStatus struct { + MerchantCode string `form:"merchantCode"` + OrderID string `form:"merchantOrderId"` + + // Transaction identification code. + // Formula: md5(merchantCode + merchantOrderId + apiKey). + Signature string `form:"signature"` +} + +func (tx *transactionStatus) sign(opts ClientOptions, paymentMethod string) { + var merchant = opts.Merchant(paymentMethod) + + tx.MerchantCode = merchant.Code + + var ( + plain = fmt.Sprintf(`%s%s%s`, tx.MerchantCode, tx.OrderID, merchant.ApiKey) + md5Plain = md5.Sum([]byte(plain)) + ) + + tx.Signature = hex.EncodeToString(md5Plain[:]) +} diff --git a/transaction_status_response.go b/transaction_status_response.go new file mode 100644 index 0000000..cba2fee --- /dev/null +++ b/transaction_status_response.go @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2023 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later + +package duitku + +// List of valid Code in TxStatusResponse. +const ( + MerchantTxStatusSuccess = `00` + MerchantTxStatusProcess = `01` + MerchantTxStatusFailed = `02` +) + +// TxStatusResponse contains response from checking merchant payment +// status. +type TxStatusResponse struct { + OrderID string `json:"merchantOrderId"` + Reference string `json:"reference"` + Amount string `json:"amount"` + + // Status code transaction. + // - 00 - Success + // - 01 - Process + // - 02 - Failed/Expired + Code string `json:"statusCode"` + + // Description that explain the status Code. + Message string `json:"statusMessage"` +} |
