diff options
| author | Shulhan <ms@kilabit.info> | 2022-11-07 11:55:20 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2022-11-07 11:59:50 +0700 |
| commit | eb3c3fc6b20b77c26a571395f584efdce8cfb4f7 (patch) | |
| tree | 0eb6fc3a9435a93f8bf6a33fa9c8cf4b7ed93b02 | |
| parent | ab25f641e3343151839cf2624d19ef235a070c9d (diff) | |
| download | duitku-eb3c3fc6b20b77c26a571395f584efdce8cfb4f7.tar.xz | |
all: implement client API for Clearing Inquiry
The ClearingInquiry method is used to initiate the transfer for Clearing
using LLG, RTGS, H2H, or BI-FAST.
| -rw-r--r-- | clearing_inquiry.go | 41 | ||||
| -rw-r--r-- | clearing_inquiry_response.go | 11 | ||||
| -rw-r--r-- | client.go | 47 | ||||
| -rw-r--r-- | client_options.go | 10 | ||||
| -rw-r--r-- | client_test.go | 35 | ||||
| -rw-r--r-- | duitku.go | 28 | ||||
| -rw-r--r-- | testdata/disbursement/clearing_inquiry_test.txt | 28 |
7 files changed, 199 insertions, 1 deletions
diff --git a/clearing_inquiry.go b/clearing_inquiry.go new file mode 100644 index 0000000..6bc7112 --- /dev/null +++ b/clearing_inquiry.go @@ -0,0 +1,41 @@ +package duitku + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "time" +) + +// ClearingInquiry contains request to initiate transfer from merchant to +// customer's bank account using [Clearing type]. +// +// For Signature it use the following formula: +// +// SHA256(email + timestamp + bankCode + type + bankAccount + amountTransfer +// + purpose + apiKey). +// +// [Clearing type]: // https://docs.duitku.com/disbursement/en/#clearing-inquiry-request +type ClearingInquiry struct { + // 9 digits customer reference number. + CustRefNumber string `json:"custRefNumber"` + + // Type of clearing: LLG, RTGS, H2H, or BIFAST. + Type string `json:"type"` + + RtolInquiry +} + +func (inq *ClearingInquiry) sign(opts ClientOptions) { + inq.UserID = opts.userID + inq.Email = opts.Email + inq.Timestamp = time.Now().UnixMilli() + + var ( + plain = fmt.Sprintf(`%s%d%s%s%s%d%s%s`, inq.Email, + inq.Timestamp, inq.BankCode, inq.Type, + inq.BankAccount, inq.Amount, inq.Purpose, opts.ApiKey) + plainHash [sha256.Size]byte = sha256.Sum256([]byte(plain)) + ) + inq.Signature = hex.EncodeToString(plainHash[:]) +} diff --git a/clearing_inquiry_response.go b/clearing_inquiry_response.go new file mode 100644 index 0000000..0fa6d98 --- /dev/null +++ b/clearing_inquiry_response.go @@ -0,0 +1,11 @@ +package duitku + +// ClearingInquiryResponse contains response from calling [Clearing Inquiry +// request]. +// +// [Clearing Inquiry request]: https://docs.duitku.com/disbursement/en/#clearing-inquiry-request +type ClearingInquiryResponse struct { + RtolInquiryResponse + + Type string `json:"type"` +} @@ -26,6 +26,13 @@ const ( PathDisbursementTransfer = `/disbursement/transfer` PathDisbursementTransferSandbox = `/disbursement/transfersandbox` // Used for testing. + + // Paths for Clearing. + PathDisbursementInquiryClearing = `/disbursement/inquiryclearing` + PathDisbursementInquiryClearingSandbox = `/disbursement/inquiryclearingsandbox` // Used for testing. + + PathDisbursementTransferClearing = `/disbursement/transferclearing` + PathDisbursementTransferClearingSandbox = `/disbursement/transferclearingsandbox` // Used for testing. ) type Client struct { @@ -85,6 +92,46 @@ func (cl *Client) CheckBalance() (bal *Balance, err error) { return bal, nil } +// ClearingInquiry initiate the transfer for Clearing using LLG, RTGS, H2H, or +// BI-FAST. +func (cl *Client) ClearingInquiry(req ClearingInquiry) (res *ClearingInquiryResponse, err error) { + var ( + logp = `ClearingInquiry` + path = PathDisbursementInquiryClearing + + httpRes *http.Response + resBody []byte + ) + + req.sign(cl.opts) + + // Since the path is different in test environment, we check the host + // here to set it. + if cl.opts.host != hostLive { + path = PathDisbursementInquiryClearingSandbox + } + + httpRes, resBody, err = cl.PostJSON(path, nil, req) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + if httpRes.StatusCode >= 500 { + return nil, fmt.Errorf(`%s: %s`, logp, httpRes.Status) + } + + fmt.Printf(`%s: resBody: %s\n`, logp, resBody) + + err = json.Unmarshal(resBody, &res) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + if res.Code != resCodeSuccess { + return nil, fmt.Errorf(`%s: %s: %s`, logp, res.Code, res.Desc) + } + + return res, nil +} + // tListBank fetch list of banks for disbursement. func (cl *Client) ListBank() (banks []Bank, err error) { var ( diff --git a/client_options.go b/client_options.go index 5ed7d2c..4915b9c 100644 --- a/client_options.go +++ b/client_options.go @@ -6,6 +6,7 @@ package duitku import ( "fmt" "net/url" + "strconv" ) // ClientOptions configuration for HTTP client. @@ -17,6 +18,9 @@ type ClientOptions struct { // The hostname extracted from ServerUrl. host string + + // The UserID converted to int64. + userID int64 } // validate each field values. @@ -34,11 +38,17 @@ func (opts *ClientOptions) validate() (err error) { if len(opts.UserID) == 0 { return fmt.Errorf(`invalid or empty UserID: %s`, opts.UserID) } + opts.userID, err = strconv.ParseInt(opts.UserID, 10, 64) + if err != nil { + return fmt.Errorf(`invalid or empty UserID: %s`, opts.UserID) + } + if len(opts.Email) == 0 { return fmt.Errorf(`invalid or empty Email: %s`, opts.Email) } if len(opts.ApiKey) == 0 { return fmt.Errorf(`invalid or empty ApiKey: %s`, opts.ApiKey) } + return nil } diff --git a/client_test.go b/client_test.go index 2e2fd8f..bcb5c3d 100644 --- a/client_test.go +++ b/client_test.go @@ -44,7 +44,40 @@ func TestClient_CheckBalance(t *testing.T) { test.Assert(t, `CheckBalance`, string(exp), string(got)) } -func TestClient_RtolInquiry_live(t *testing.T) { +func TestClient_ClearingInquiry_sandbox(t *testing.T) { + t.Skip(`This test require external call to server`) + + var ( + inquiryReq ClearingInquiry + err error + tdata *test.Data + inquiryRes *ClearingInquiryResponse + ) + + tdata, err = test.LoadData(`testdata/disbursement/clearing_inquiry_test.txt`) + if err != nil { + t.Fatal(err) + } + + err = json.Unmarshal(tdata.Input[`request.json`], &inquiryReq) + if err != nil { + t.Fatal(err) + } + + inquiryRes, err = testClient.ClearingInquiry(inquiryReq) + if err != nil { + t.Fatal(err) + } + + // We cannot compare the response, because for each call to server + // it will return different DisburseID. + + t.Logf(`inquiryRes: %+v`, inquiryRes) + + test.Assert(t, `AccountName`, `Test Account`, inquiryRes.AccountName) +} + +func TestClient_RtolInquiry_sandbox(t *testing.T) { t.Skip(`This test require external call to server`) var ( @@ -12,3 +12,31 @@ const ( hostLive = `passport.duitku.com` ) + +const ( + // LLG (Lalu Lintas Giro) is interbank transfer that cover more than + // 130 bank in Indonesia. + // The maximal amount transfer is IDR 500.000.000. + // Transfer process follows the BI (Bank Indonesia) schedule, which is + // 8.00-15.00 on business days. + ClearingTypeLLG = `LLG` + + // RTGS (Real Time Gross Settlement) is interbank transfer that cover + // more than 130 bank in Indonesia. + // The minimal amount transfer is IDR 100.000.000. + // Transfer process follows the BI (Bank Indonesia) schedule, which is + // 8.00-15.00 on business days. + ClearingTypeRTGS = `RTGS` + + // H2H (Bank Host to Host) Duitku Host to Host connection to bank, to + // ensure direct connection and better reliability. + // Currently only support 4 Major banks in Indonesia (BNI, BRI, + // Mandiri, Permata). + // Transfer schedule follows the schedule of each bank. + ClearingTypeH2H = `H2H` + + // BIFAST (Bank Indonesia Fast Payment) National retail payments that + // can facilitate retail payments in real-time, safe, efficient, more + // affordable service fees and available at any time (24/7). + ClearingTypeBIFAST = `BIFAST` +) diff --git a/testdata/disbursement/clearing_inquiry_test.txt b/testdata/disbursement/clearing_inquiry_test.txt new file mode 100644 index 0000000..63581e7 --- /dev/null +++ b/testdata/disbursement/clearing_inquiry_test.txt @@ -0,0 +1,28 @@ +>>> request.json +{ + "userId": 3551, + "email": "test@chakratechnology.com", + "amountTransfer": 10000, + "bankCode": "014", + "bankAccount": "8760673466", + "purpose": "Test Clearing Inquiry with duitku.", + "timestamp": 1506486841000, + "custRefNumber": "12345789", + "senderId": 123456789, + "senderName": "John Doe", + "type": "LLG" +} + +<<< response.json +{ + "email": "test@chakratechnology.com", + "bankCode": "014", + "bankAccount": "8760673466", + "amountTransfer": 10000, + "accountName": "Test Account", + "custRefNumber": "12345789", + "disburseId": 121012, + "type": "LLG", + "responseCode": "00", + "responseDesc": "Approved or completed successfully" +} |
