aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2022-11-04 19:21:35 +0700
committerShulhan <ms@kilabit.info>2022-11-04 19:23:41 +0700
commit84e72cd4f383db1bfbf8b04471911217538bd49d (patch)
tree9eb695ea02dee1562d9ed509df7aa266bd34e6a5
parent0eef440900eda71b3a257707e49e0ad53be5353f (diff)
downloadduitku-84e72cd4f383db1bfbf8b04471911217538bd49d.tar.xz
all: implement API to do online transfer
The RtolTransfer do the actual transfer to customer's bank account using the request and response from RtolInquiry.
-rw-r--r--client.go59
-rw-r--r--client_test.go39
-rw-r--r--rtol_transfer.go92
-rw-r--r--rtol_transfer_response.go15
-rw-r--r--testdata/disbursement_rtol_transfer_test.txt16
5 files changed, 219 insertions, 2 deletions
diff --git a/client.go b/client.go
index 8768a5b..7bdc272 100644
--- a/client.go
+++ b/client.go
@@ -15,13 +15,17 @@ import (
libhttp "github.com/shuLhan/share/lib/http"
)
+// List of known and implemented HTTP API paths.
const (
PathDisbursementListBank = `/disbursement/listBank`
PathDisbursementCheckBalance = `/disbursement/checkBalance`
- // Path for transfer online.
+ // Paths for transfer online.
PathDisbursementInquiry = `/disbursement/inquiry`
- PathDisbursementInquirySandbox = `/disbursement/inquirysandbox` // Used when server URL is sandbox (testing).
+ PathDisbursementInquirySandbox = `/disbursement/inquirysandbox` // Used for testing.
+
+ PathDisbursementTransfer = `/disbursement/transfer`
+ PathDisbursementTransferSandbox = `/disbursement/transfersandbox` // Used for testing.
)
type Client struct {
@@ -182,3 +186,54 @@ func (cl *Client) RtolInquiry(req RtolInquiry) (res *RtolInquiryResponse, err er
return res, nil
}
+
+// RtolTransfer do the actual transfer to customer's bank account using the
+// request and response from call to RtolInquiry.
+//
+// Transfer will be limited from 25 to 50 Million per transaction depending on
+// the beneficiary bank account.
+//
+// Ref: https://docs.duitku.com/disbursement/en/#online-transfer-transfer-request
+func (cl *Client) RtolTransfer(inquiryReq RtolInquiry, inquiryRes RtolInquiryResponse) (res *RtolTransferResponse, err error) {
+ var (
+ logp = `RtolTransfer`
+ path = PathDisbursementTransfer
+
+ req *rtolTransfer
+ resHttp *http.Response
+ resBody []byte
+ )
+
+ // Since the path is different in test environment, we check the host
+ // here to set it.
+ if cl.opts.host != hostLive {
+ path = PathDisbursementTransferSandbox
+ }
+
+ req = newRtolTransfer(&inquiryReq, &inquiryRes)
+
+ err = req.sign(cl.opts)
+ if err != nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ 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: %s`, logp, resHttp.Status)
+ }
+
+ fmt.Printf(`%s: %s`, 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
+}
diff --git a/client_test.go b/client_test.go
index 09db153..e1fdc4d 100644
--- a/client_test.go
+++ b/client_test.go
@@ -77,6 +77,45 @@ func TestClient_RtolInquiry_live(t *testing.T) {
test.Assert(t, `AccountName`, `Test Account`, inquiryRes.AccountName)
}
+func TestClient_RtolTransfer_sandbox(t *testing.T) {
+ t.Skip(`This test require external call to server`)
+
+ var (
+ err error
+ tdata *test.Data
+ inquiryReq *RtolInquiry
+ inquiryRes *RtolInquiryResponse
+ transferRes *RtolTransferResponse
+ )
+
+ tdata, err = test.LoadData(`testdata/disbursement_rtol_transfer_test.txt`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = json.Unmarshal(tdata.Input[`request_inquiry.json`], &inquiryReq)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ inquiryRes, err = testClient.RtolInquiry(*inquiryReq)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ transferRes, err = testClient.RtolTransfer(*inquiryReq, *inquiryRes)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // We cannot compare the response, because for each call to server
+ // it will return different CustRefNumber and DisburseID.
+
+ t.Logf(`RtolTransfer response: %+v`, transferRes)
+
+ test.Assert(t, `AccountName`, `Test Account`, inquiryRes.AccountName)
+}
+
func TestClient_DisbursementListBank(t *testing.T) {
var (
tdata *test.Data
diff --git a/rtol_transfer.go b/rtol_transfer.go
new file mode 100644
index 0000000..db6eabc
--- /dev/null
+++ b/rtol_transfer.go
@@ -0,0 +1,92 @@
+package duitku
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "strconv"
+ "time"
+)
+
+// rtolTransfer containts request to transfer amount from merchant to
+// customer's bank account, using the previous data obtained from the inquiry
+// process.
+//
+// The formula to generate Signature is SHA256(email + timestamp + bankCode +
+// bankAccount + accountName + custRefNumber + amountTransfer + purpose +
+// disburseId + secretKey).
+//
+// Ref: https://docs.duitku.com/disbursement/en/#online-transfer-transfer-request
+type rtolTransfer struct {
+ // Bank Account owner, obtained after getting a response from the
+ // inquiry process.
+ AccountName string `json:"accountName"`
+
+ // Destination Bank Code sent when inquiry process.
+ BankCode string `json:"bankCode"`
+
+ // Destination account number sent when inquiry procces.
+ BankAccount string `json:"bankAccount"`
+
+ // Customer reference number, obtained after getting a response from
+ // the inquiry process.
+ CustRefNumber string `json:"custRefNumber"`
+
+ // Description of transfer purpose.
+ Purpose string `json:"purpose"`
+
+ request
+
+ // Disbursement transfer amount.
+ Amount int64 `json:"amountTransfer"`
+
+ // Disbursement Id provided by Duitku, obtained after getting a
+ // response from the inquiry process.
+ DisburseID int64 `json:"disburseId"`
+}
+
+// newRtolTransfer create new rtolTransfer request from request and response
+// of RtolInquiry.
+func newRtolTransfer(inquiryReq *RtolInquiry, inquiryRes *RtolInquiryResponse) (req *rtolTransfer) {
+ req = &rtolTransfer{
+ Amount: inquiryReq.Amount,
+ Purpose: inquiryReq.Purpose,
+
+ AccountName: inquiryRes.AccountName,
+ BankCode: inquiryRes.BankCode,
+ BankAccount: inquiryRes.BankAccount,
+ CustRefNumber: inquiryRes.CustRefNumber,
+ DisburseID: inquiryRes.DisburseID,
+ }
+ return req
+}
+
+func (req *rtolTransfer) sign(opts ClientOptions) (err error) {
+ var (
+ logp = `sign`
+ now = time.Now()
+
+ bb bytes.Buffer
+ plainHash [sha256.Size]byte
+ )
+
+ req.UserID, err = strconv.ParseInt(opts.UserID, 10, 64)
+ if err != nil {
+ return fmt.Errorf(`%s: %s`, logp, err)
+ }
+
+ req.Email = opts.Email
+ req.Timestamp = now.UnixMilli()
+
+ fmt.Fprintf(&bb, `%s%d%s%s%s%s%d%s%d%s`, req.Email, req.Timestamp,
+ req.BankCode, req.BankAccount, req.AccountName,
+ req.CustRefNumber, req.Amount, req.Purpose,
+ req.DisburseID, opts.ApiKey)
+
+ plainHash = sha256.Sum256(bb.Bytes())
+
+ req.Signature = hex.EncodeToString(plainHash[:])
+
+ return nil
+}
diff --git a/rtol_transfer_response.go b/rtol_transfer_response.go
new file mode 100644
index 0000000..3ce6eab
--- /dev/null
+++ b/rtol_transfer_response.go
@@ -0,0 +1,15 @@
+package duitku
+
+import "github.com/shuLhan/share/lib/math/big"
+
+// RtolTransferResponse contains response from online transfer.
+type RtolTransferResponse struct {
+ Email string `json:"email"`
+ BankCode string `json:"bankCode"`
+ BankAccount string `json:"bankAccount"`
+ AccountName string `json:"accountName"`
+ CustRefNumber string `json:"custRefNumber"`
+ Amount *big.Rat `json:"amountTransfer"`
+
+ response
+}
diff --git a/testdata/disbursement_rtol_transfer_test.txt b/testdata/disbursement_rtol_transfer_test.txt
new file mode 100644
index 0000000..f9a0d82
--- /dev/null
+++ b/testdata/disbursement_rtol_transfer_test.txt
@@ -0,0 +1,16 @@
+Test disbursement online transfer.
+
+Ref: https://docs.duitku.com/disbursement/en/#transfer-online
+
+>>> request_inquiry.json
+{
+ "userId": 3551,
+ "email": "test@chakratechnology.com",
+ "amountTransfer": 10000,
+ "bankAccount": "8760673566",
+ "bankCode": "002",
+ "purpose": "Test Transfer Online Inquiry with duitku.",
+ "timestamp": 1506486841000,
+ "senderId": 123456789,
+ "senderName": "John Doe"
+}