├── .gitignore ├── LICENSE ├── README.md ├── bank.go ├── coreapi ├── client.go ├── client_test.go ├── clientsubscription.go ├── clientsubscription_test.go ├── clienttokenization.go ├── clienttokenization_test.go ├── clienttransaction.go ├── clienttransaction_test.go ├── paymenttype.go ├── request.go └── response.go ├── error.go ├── error_test.go ├── example ├── README.md ├── mockup.go └── simple │ ├── coreapi-card-3ds │ ├── main.go │ └── views │ │ └── index.html │ ├── coreapi │ └── sample.go │ ├── iris │ └── sample.go │ ├── snap │ └── sample.go │ ├── subscriptions │ └── sample.go │ ├── tokenization │ └── sample.go │ └── transaction │ └── sample.go ├── go.mod ├── go.sum ├── httpclient.go ├── iris ├── client.go ├── client_test.go ├── request.go └── response.go ├── log.go ├── maintaining.md ├── midtrans.go ├── request.go └── snap ├── client.go ├── client_test.go ├── paymenttype.go ├── request.go └── response.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Goland 9 | .idea 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Midtrans 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Midtrans Go Library 2 | [](https://goreportcard.com/report/github.com/midtrans/midtrans-go) 3 | [](https://opensource.org/licenses/MIT) 4 | 5 | Midtrans :heart: Go ! 6 | 7 | Go is a very modern, terse, and combine aspect of dynamic and static typing that in a way very well suited for web development, among other things. 8 | Its small memory footprint is also an advantage of itself. This module will help you use Midtrans product's REST APIs in Go. 9 | 10 | ## 1. Installation 11 | ### 1.1 Using Go Module 12 | Run this command on your project to initialize Go mod (if you haven't): 13 | ```go 14 | go mod init 15 | ``` 16 | then reference midtrans-go in your project file with `import`: 17 | ```go 18 | import ( 19 | "github.com/midtrans/midtrans-go" 20 | "github.com/midtrans/midtrans-go/coreapi" 21 | "github.com/midtrans/midtrans-go/snap" 22 | "github.com/midtrans/midtrans-go/iris" 23 | ) 24 | ``` 25 | 26 | ### 1.2 Using go get 27 | Also, the alternative way you can use `go get` the package into your project 28 | ```go 29 | go get -u github.com/midtrans/midtrans-go 30 | ``` 31 | ## 2. Usage 32 | There is a type named `Client` (`coreapi.Client`, `snap.Client`, `iris.Client`) that should be instantiated through 33 | function `New` which holds any possible setting to the library. Any activity (charge, approve, etc) is done in the client level. 34 | 35 | ### 2.1 Choose Product/Method 36 | We have [3 different products](https://beta-docs.midtrans.com/) that you can use: 37 | - [Snap](#22A-snap) - Customizable payment popup will appear on **your web/app** (no redirection). [doc ref](https://snap-docs.midtrans.com/) 38 | - [Snap Redirect](#22B-snap-redirect) - Customer need to be redirected to payment url **hosted by midtrans**. [doc ref](https://snap-docs.midtrans.com/) 39 | - [Core API (VT-Direct)](#22C-core-api-vt-direct) - Basic backend implementation, you can customize the frontend embedded on **your web/app** as you like (no redirection). [doc ref](https://api-docs.midtrans.com/) 40 | - [Iris Disbursement](#22D-iris-api) - Iris is Midtrans’ cash management solution that allows you to disburse payments to any bank accounts in Indonesia securely and easily. [doc ref](https://iris-docs.midtrans.com/) 41 | 42 | To learn more and understand each of the product's quick overview you can visit https://docs.midtrans.com. 43 | 44 | 45 | ### 2.2 Client Initialization and Configuration 46 | Get your client key and server key from [Midtrans Dashboard](https://dashboard.midtrans.com) 47 | 48 | Create API client object, You can also check the [project's implementation](example/simple) for more examples. Please proceed there for more detail on how to run the example. 49 | 50 | #### 2.2.1 Using global config 51 | Set a config with globally, (except for iris api) 52 | 53 | ```go 54 | midtrans.ServerKey = "YOUR-SERVER-KEY" 55 | midtrans.Environment = midtrans.Sandbox 56 | ``` 57 | 58 | #### 2.2.2 Using Client 59 | ```go 60 | //Initiate client for Midtrans CoreAPI 61 | var c = coreapi.Client 62 | c.New("YOUR-SERVER-KEY", midtrans.Sandbox) 63 | 64 | //Initiate client for Midtrans Snap 65 | var s = snap.Client 66 | s.New("YOUR-SERVER-KEY", midtrans.Sandbox) 67 | 68 | //Initiate client for Iris disbursement 69 | var i = iris.Client 70 | i.New("IRIS-API-KEY", midtrans.Sandbox) 71 | ``` 72 | ### 2.3 Snap 73 | Snap is Midtrans existing tool to help merchant charge customers using a mobile-friendly, in-page, 74 | no-redirect checkout facilities. [Using snap is simple](https://docs.midtrans.com/en/snap/overview). 75 | 76 | Available methods for Snap 77 | ```go 78 | // CreateTransaction : Do `/transactions` API request to SNAP API to get Snap token and redirect url with `snap.Request` 79 | func CreateTransaction(req *snap.Request) (*Response, *midtrans.Error) 80 | 81 | // CreateTransactionToken : Do `/transactions` API request to SNAP API to get Snap token with `snap.Request` 82 | func CreateTransactionToken(req *snap.Request) (string, *midtrans.Error) 83 | 84 | // CreateTransactionUrl : Do `/transactions` API request to SNAP API to get Snap redirect url with `snap.Request` 85 | func CreateTransactionUrl(req *snap.Request) (string, *midtrans.Error) 86 | 87 | // CreateTransactionWithMap : Do `/transactions` API request to SNAP API to get Snap token and redirect url with Map request 88 | func CreateTransactionWithMap(req *snap.RequestParamWithMap) (ResponseWithMap, *midtrans.Error) 89 | 90 | // CreateTransactionTokenWithMap : Do `/transactions` API request to SNAP API to get Snap token with Map request 91 | func CreateTransactionTokenWithMap(req *snap.RequestParamWithMap) (string, *midtrans.Error) 92 | 93 | // CreateTransactionUrlWithMap : Do `/transactions` API request to SNAP API to get Snap redirect url with Map request 94 | func CreateTransactionUrlWithMap(req *snap.RequestParamWithMap) (string, *midtrans.Error) 95 | ``` 96 | Snap usage example, create transaction with minimum Snap parameters (choose **one** of alternatives below): 97 | #### 2.3.1 Using global Config & static function 98 | Sample usage if you prefer Midtrans global configuration & using static function. Useful if you only use 1 merchant account API key, and keep the code short. 99 | ```go 100 | // 1. Set you ServerKey with globally 101 | midtrans.ServerKey = "YOUR-SERVER-KEY" 102 | midtrans.Environment = midtrans.Sandbox 103 | 104 | // 2. Initiate Snap request 105 | req := & snap.RequestParam{ 106 | TransactionDetails: midtrans.TransactionDetails{ 107 | OrderID: "YOUR-ORDER-ID-12345", 108 | GrossAmt: 100000, 109 | }, 110 | CreditCard: &snap.CreditCardDetails{ 111 | Secure: true, 112 | }, 113 | } 114 | 115 | // 3. Request create Snap transaction to Midtrans 116 | snapResp, _ := CreateTransaction(req) 117 | fmt.Println("Response :", snapResp) 118 | ``` 119 | #### 2.3.2 Using Client 120 | Sample usage if you prefer to use client instance & config. Useful if you plan to use multiple merchant account API keys, want to have multiple client instances, or prefer the code to be object-oriented. 121 | 122 | ```go 123 | // 1. Initiate Snap client 124 | var s = snap.Client 125 | s.New("YOUR-SERVER-KEY", midtrans.Sandbox) 126 | 127 | // 2. Initiate Snap request 128 | req := & snap.RequestParam{ 129 | TransactionDetails: midtrans.TransactionDetails{ 130 | OrderID: "YOUR-ORDER-ID-12345", 131 | GrossAmt: 100000, 132 | }, 133 | CreditCard: &snap.CreditCardDetails{ 134 | Secure: true, 135 | }, 136 | } 137 | 138 | // 3. Request create Snap transaction to Midtrans 139 | snapResp, _ := s.CreateTransaction(req) 140 | fmt.Println("Response :", snapResp) 141 | ``` 142 | 143 | On the frontend side (on the HTML payment page), you will [need to include snap.js library and implement the payment page](https://docs.midtrans.com/en/snap/integration-guide?id=_2-displaying-snap-payment-page-on-frontend). 144 | 145 | Sample HTML payment page implementation: 146 | ```html 147 | 148 |
149 | 150 |151 | 152 | 153 | 154 | 173 | 174 | 175 | ``` 176 | 177 | You may want to override those `onSuccess`, `onPending` and `onError` functions to implement the behaviour that you want on each respective event. 178 | 179 | Then implement Backend Notification Handler, [Refer to this section](README.md#26-handle-http-notification) 180 | 181 | Alternativelly, more complete Snap parameter: 182 | 183 | ```go 184 | func GenerateSnapReq() *snap.Request { 185 | // Initiate Customer address 186 | custAddress := &midtrans.CustomerAddress{ 187 | FName: "John", 188 | LName: "Doe", 189 | Phone: "081234567890", 190 | Address: "Baker Street 97th", 191 | City: "Jakarta", 192 | Postcode: "16000", 193 | CountryCode: "IDN", 194 | } 195 | 196 | // Initiate Snap Request 197 | snapReq := &snap.Request{ 198 | TransactionDetails: midtrans.TransactionDetails{ 199 | OrderID: "YOUR-UNIQUE-ORDER-ID-1234", 200 | GrossAmt: 200000, 201 | }, 202 | CreditCard: &snap.CreditCardDetails{ 203 | Secure: true, 204 | }, 205 | CustomerDetail: &midtrans.CustomerDetails{ 206 | FName: "John", 207 | LName: "Doe", 208 | Email: "john@doe.com", 209 | Phone: "081234567890", 210 | BillAddr: custAddress, 211 | ShipAddr: custAddress, 212 | }, 213 | Items: &[]midtrans.ItemDetails{ 214 | midtrans.ItemDetails{ 215 | ID: "ITEM1", 216 | Price: 200000, 217 | Qty: 1, 218 | Name: "Someitem", 219 | }, 220 | }, 221 | } 222 | 223 | return snapReq 224 | } 225 | ``` 226 | 227 | >**INFO:** 228 | > When using client, you can set config options like `SetIdempotencyKey`, `SetContext`, `SetPaymentOverrideNotif`, etc 229 | > from Options object on the client, [check the usage detail on how to configure options here](README.md#3-advance-usage) 230 | 231 | #### Alternative, perform Core API Charge with Map type 232 | Snap client have `...WithMap` function, which is useful if you want to send custom JSON payload that the type/struct is not defined in this module. Refer to file `sample.go` in folder [Snap API simple sample](example/simple/snap/sample.go). 233 | 234 | ### 2.4 CoreApi 235 | Available methods for `CoreApi` 236 | ```go 237 | // ChargeTransaction : Do `/charge` API request to Midtrans Core API return `coreapi.Response` with `coreapi.ChargeReq` 238 | func ChargeTransaction(req *ChargeReq) (*Response, *midtrans.Error) 239 | 240 | // ChargeTransactionWithMap : Do `/charge` API request to Midtrans Core API return RAW MAP with Map as 241 | func ChargeTransactionWithMap(req *ChargeReqWithMap) (ResponseWithMap, *midtrans.Error) 242 | 243 | // CardToken : Do `/token` API request to Midtrans Core API return `coreapi.Response`, 244 | func CardToken(cardNumber string, expMonth int, expYear int, cvv string) (*CardTokenResponse, *midtrans.Error) 245 | 246 | // RegisterCard : Do `/card/register` API request to Midtrans Core API return `coreapi.Response`, 247 | func RegisterCard(cardNumber string, expMonth int, expYear int, cvv string) (*CardRegisterResponse, *midtrans.Error) 248 | 249 | // CardPointInquiry : Do `/point_inquiry/{tokenId}` API request to Midtrans Core API return `coreapi.Response`, 250 | func CardPointInquiry(cardToken string) (*CardTokenResponse, *midtrans.Error) 251 | 252 | // GetBIN : Do `/v1/bins/{bin}` API request to Midtrans Core API return `coreapi.BinResponse`, 253 | func GetBIN(binNumber string) (*BinResponse, *midtrans.Error) 254 | 255 | // CheckTransaction : Do `/{orderId}/status` API request to Midtrans Core API return `coreapi.Response`, 256 | func CheckTransaction(param string) (*Response, *midtrans.Error) 257 | 258 | // ApproveTransaction : Do `/{orderId}/approve` API request to Midtrans Core API return `coreapi.Response`, 259 | func ApproveTransaction(param string) (*Response, *midtrans.Error) 260 | 261 | // DenyTransaction : Do `/{orderId}/deny` API request to Midtrans Core API return `coreapi.Response`, 262 | func DenyTransaction(param string) (*Response, *midtrans.Error) 263 | 264 | // CancelTransaction : Do `/{orderId}/cancel` API request to Midtrans Core API return `coreapi.Response`, 265 | func CancelTransaction(param string) (*Response, *midtrans.Error) 266 | 267 | // ExpireTransaction : Do `/{orderId}/expire` API request to Midtrans Core API return `coreapi.Response`, 268 | func ExpireTransaction(param string) (*Response, *midtrans.Error) 269 | 270 | // RefundTransaction : Do `/{orderId}/refund` API request to Midtrans Core API return `coreapi.Response`, 271 | // with `coreapi.RefundReq` as body parameter, will be converted to JSON, 272 | func RefundTransaction(param string, req *RefundReq) (*Response, *midtrans.Error) 273 | 274 | // DirectRefundTransaction : Do `/{orderId}/refund/online/direct` API request to Midtrans Core API return `coreapi.Response`, 275 | // with `coreapi.RefundReq` as body parameter, will be converted to JSON, 276 | func DirectRefundTransaction(param string, req *RefundReq) (*Response, *midtrans.Error) 277 | 278 | // CaptureTransaction : Do `/{orderId}/capture` API request to Midtrans Core API return `coreapi.Response`, 279 | // with `coreapi.CaptureReq` as body parameter, will be converted to JSON, 280 | func CaptureTransaction(req *CaptureReq) (*Response, *midtrans.Error) 281 | 282 | // GetStatusB2B : Do `/{orderId}/status/b2b` API request to Midtrans Core API return `coreapi.Response`, 283 | func GetStatusB2B(param string) (*Response, *midtrans.Error) 284 | ``` 285 | #### 2.4.1 Using global Config & static function 286 | Sample usage if you prefer Midtrans global configuration & using static function. Useful if you only use 1 merchant account API key, and keep the code short. 287 | 288 | ```go 289 | // 1. Set you ServerKey with globally 290 | midtrans.ServerKey = "YOUR-SERVER-KEY" 291 | midtrans.Environment = midtrans.Sandbox 292 | 293 | // 2. Initiate charge request 294 | chargeReq := &coreapi.ChargeReq{ 295 | PaymentType: coreapi.PaymentTypeCreditCard, 296 | TransactionDetails: midtrans.TransactionDetails{ 297 | OrderID: "12345", 298 | GrossAmt: 200000, 299 | }, 300 | CreditCard: &coreapi.CreditCardDetails{ 301 | TokenID: "YOUR-CC-TOKEN", 302 | Authentication: true, 303 | }, 304 | Items: &[]midtrans.ItemDetails{ 305 | { 306 | ID: "ITEM1", 307 | Price: 200000, 308 | Qty: 1, 309 | Name: "Someitem", 310 | }, 311 | }, 312 | } 313 | 314 | // 3. Request to Midtrans using global config 315 | coreApiRes, _ := coreapi.ChargeTransaction(chargeReq) 316 | fmt.Println("Response :", coreApiRes) 317 | ``` 318 | #### 2.4.2 Using Client 319 | Sample usage if you prefer to use client instance & config. Useful if you plan to use multiple merchant account API keys, want to have multiple client instances, or prefer the code to be object-oriented. 320 | 321 | ```go 322 | // 1. Initiate coreapi client 323 | c := coreapi.Client{} 324 | c.New("YOUR-SERVER-KEY", midtrans.Sandbox) 325 | 326 | // 2. Initiate charge request 327 | chargeReq := &coreapi.ChargeReq{ 328 | PaymentType: midtrans.SourceCreditCard, 329 | TransactionDetails: midtrans.TransactionDetails{ 330 | OrderID: "12345", 331 | GrossAmt: 200000, 332 | }, 333 | CreditCard: &coreapi.CreditCardDetails{ 334 | TokenID: "YOUR-CC-TOKEN", 335 | Authentication: true, 336 | }, 337 | Items: &[]midtrans.ItemDetail{ 338 | coreapi.ItemDetail{ 339 | ID: "ITEM1", 340 | Price: 200000, 341 | Qty: 1, 342 | Name: "Someitem", 343 | }, 344 | }, 345 | } 346 | 347 | // 3. Request to Midtrans 348 | coreApiRes, _ := c.ChargeTransaction(chargeReq) 349 | fmt.Println("Response :", coreApiRes) 350 | ``` 351 | >**INFO:** 352 | > When using client, you can set config options like `SetIdempotencyKey`, `SetContext`, `SetPaymentOverrideNotif`, etc 353 | > from Options object on the client, [check the usage detail on how to configure options here](README.md#3-advance-usage) 354 | 355 | #### Alternative, perform Core API Charge with Map type 356 | CoreApi client have `ChargeTransactionWithMap` function, which is useful if you want to send custom JSON payload that the type/struct is not defined in this module. Refer to file `sample.go` in folder [Core API simple sample](example/simple/coreapi/sample.go). 357 | 358 | ### 2.5 Iris Client 359 | Iris is Midtrans cash management solution that allows you to disburse payments to any supported bank accounts securely and easily. Iris connects to the banks’ hosts to enable seamless transfer using integrated APIs. 360 | Available methods for `Iris` 361 | ```go 362 | // CreateBeneficiaries : to perform create a new beneficiary information for quick access on the payout page in Iris Portal. 363 | func (c Client) CreateBeneficiaries(req Beneficiaries) (*BeneficiariesResponse, *midtrans.Error) 364 | 365 | // UpdateBeneficiaries : to update an existing beneficiary identified by its alias_name. 366 | func (c Client) UpdateBeneficiaries(aliasName string, req Beneficiaries) (*BeneficiariesResponse, *midtrans.Error) 367 | 368 | // GetBeneficiaries : This method to fetch list of all beneficiaries saved in Iris Portal. 369 | func (c Client) GetBeneficiaries() ([]Beneficiaries, *midtrans.Error) 370 | 371 | // CreatePayout : This method for Creator to create a payout. It can be used for single payout and also multiple payouts. 372 | func (c Client) CreatePayout(req CreatePayoutReq) (*CreatePayoutResponse, *midtrans.Error) 373 | 374 | // ApprovePayout : this method for Apporver to approve multiple payout request. 375 | func (c Client) ApprovePayout(req ApprovePayoutReq) (*ApprovePayoutResponse, *midtrans.Error) 376 | 377 | // RejectPayout : This method for Apporver to reject multiple payout request. 378 | func (c Client) RejectPayout(req RejectPayoutReq) (*RejectPayoutResponse, *midtrans.Error) 379 | 380 | // GetPayoutDetails : Get details of a single payout. 381 | func (c Client) GetPayoutDetails(referenceNo string) (*PayoutDetailResponse, *midtrans.Error) 382 | 383 | // GetTransactionHistory : Returns all the payout details for specific dates 384 | func (c Client) GetTransactionHistory(fromDate string, toDate string) ([]TransactionHistoryResponse, *midtrans.Error) 385 | 386 | // GetTopUpChannels : Provide top up information channel for Aggregator Partner 387 | func (c Client) GetTopUpChannels() ([]TopUpAccountResponse, *midtrans.Error) 388 | 389 | // GetBalance : For Aggregator Partner, you need to top up to Iris’ bank account. Every partner have their own balance in Iris’ 390 | // bank account. Use this API is to get current balance information. 391 | func (c Client) GetBalance() (*BalanceResponse, *midtrans.Error) 392 | 393 | // GetListBankAccount : Show list of registered bank accounts for facilitator partner 394 | func (c Client) GetListBankAccount() ([]BankAccountResponse, *midtrans.Error) 395 | 396 | // GetFacilitatorBalance : For Facilitator Partner, use this API is to get current balance information of your registered bank account. 397 | func (c Client) GetFacilitatorBalance(accountId string) (*BalanceResponse, *midtrans.Error) 398 | 399 | // GetBeneficiaryBanks : Show list of supported banks in IRIS. 400 | func (c Client) GetBeneficiaryBanks() (*ListBeneficiaryBankResponse, *midtrans.Error) 401 | 402 | // ValidateBankAccount : Check if an account is valid, if valid return account information. 403 | func (c Client) ValidateBankAccount(bankName string, accountNo string) (*BankAccountDetailResponse, *midtrans.Error) 404 | ``` 405 | 406 | >Note: `IrisApiKey` will be used in `Iris.Client`'s the API Key can be found in Iris Dashboard. The API Key is different to Midtrans' payment gateway account's API key. 407 | ```go 408 | var i iris.Client 409 | i.New("YOUR-IRIS-API-KEY", midtrans.Sandbox) 410 | 411 | res, _ := i.GetBeneficiaryBanks() 412 | fmt.Println("Response: ", res) 413 | ``` 414 | 415 | ### 2.6 Handle HTTP Notification 416 | Create separated web endpoint (notification url) to receive HTTP POST notification callback/webhook. 417 | HTTP notification will be sent whenever transaction status is changed. 418 | Example also available in `sample.go` in folder [example/simple/coreapi](example/simple/coreapi/sample.go) 419 | 420 | ```go 421 | func notification(w http.ResponseWriter, r *http.Request) { 422 | // 1. Initialize empty map 423 | var notificationPayload map[string]interface{} 424 | 425 | // 2. Parse JSON request body and use it to set json to payload 426 | err := json.NewDecoder(r.Body).Decode(¬ificationPayload) 427 | if err != nil { 428 | // do something on error when decode 429 | return 430 | } 431 | // 3. Get order-id from payload 432 | orderId, exists := notificationPayload["order_id"].(string) 433 | if !exists { 434 | // do something when key `order_id` not found 435 | return 436 | } 437 | 438 | // 4. Check transaction to Midtrans with param orderId 439 | transactionStatusResp, e := c.CheckTransaction(orderId) 440 | if e != nil { 441 | http.Error(w, e.GetMessage(), http.StatusInternalServerError) 442 | return 443 | } else { 444 | if transactionStatusResp != nil { 445 | // 5. Do set transaction status based on response from check transaction status 446 | if transactionStatusResp.TransactionStatus == "capture" { 447 | if transactionStatusResp.FraudStatus == "challenge" { 448 | // TODO set transaction status on your database to 'challenge' 449 | // e.g: 'Payment status challenged. Please take action on your Merchant Administration Portal 450 | } else if transactionStatusResp.FraudStatus == "accept" { 451 | // TODO set transaction status on your database to 'success' 452 | } 453 | } else if transactionStatusResp.TransactionStatus == "settlement" { 454 | // TODO set transaction status on your databaase to 'success' 455 | } else if transactionStatusResp.TransactionStatus == "deny" { 456 | // TODO you can ignore 'deny', because most of the time it allows payment retries 457 | // and later can become success 458 | } else if transactionStatusResp.TransactionStatus == "cancel" || transactionStatusResp.TransactionStatus == "expire" { 459 | // TODO set transaction status on your databaase to 'failure' 460 | } else if transactionStatusResp.TransactionStatus == "pending" { 461 | // TODO set transaction status on your databaase to 'pending' / waiting payment 462 | } 463 | } 464 | } 465 | w.Header().Set("Content-Type", "application/json") 466 | w.Write([]byte("ok")) 467 | } 468 | ``` 469 | ### 2.7 Transaction Action 470 | Other functions related to actions that can be performed to transaction(s). Also available as examples [here](example/simple/transaction/sample.go) 471 | #### Get Status 472 | ```go 473 | // get status of transaction that already recorded on midtrans (already `charge`-ed) 474 | res, _ := c.CheckTransaction("YOUR_ORDER_ID OR TRANSACTION_ID") 475 | if res != nil { 476 | // do something to `res` object 477 | } 478 | ``` 479 | #### Get Status B2B 480 | ```go 481 | // get transaction status of VA b2b transaction 482 | res, _ := c.GetStatusB2B("YOUR_ORDER_ID OR TRANSACTION_ID") 483 | if res != nil { 484 | // do something to `res` object 485 | } 486 | ``` 487 | #### Approve Transaction 488 | ```go 489 | // approve a credit card transaction with `challenge` fraud status 490 | res, _ := c.ApproveTransaction("YOUR_ORDER_ID OR TRANSACTION_ID") 491 | if res != nil { 492 | // do something to `res` object 493 | } 494 | ``` 495 | #### Deny Transaction 496 | ```go 497 | // deny a credit card transaction with `challenge` fraud status 498 | res, _ := c.DenyTransaction("YOUR_ORDER_ID OR TRANSACTION_ID") 499 | if res != nil { 500 | // do something to `res` object 501 | } 502 | ``` 503 | #### Cancel Transaction 504 | ```go 505 | // cancel a credit card transaction or pending transaction 506 | res, _ := c.CancelTransaction("YOUR_ORDER_ID OR TRANSACTION_ID") 507 | if res != nil { 508 | // do something to `res` object 509 | } 510 | ``` 511 | #### Capture Transaction 512 | ```go 513 | // Capture an authorized transaction for card payment 514 | refundRequest := &coreapi.CaptureReq{ 515 | TransactionID: "TRANSACTION-ID", 516 | GrossAmt: 10000, 517 | } 518 | res, _ := c.CaptureTransaction(refundRequest) 519 | if res != nil { 520 | // do something to `res` object 521 | } 522 | ``` 523 | #### Expire Transaction 524 | ```go 525 | // expire a pending transaction 526 | res, _ := c.ExpireTransaction("YOUR_ORDER_ID OR TRANSACTION_ID") 527 | if res != nil { 528 | // do something to `res` object 529 | } 530 | ``` 531 | #### Refund Transaction 532 | ```go 533 | refundRequest := &coreapi.RefundReq{ 534 | Amount: 5000, 535 | Reason: "Item out of stock", 536 | } 537 | 538 | res, _ := c.RefundTransaction("YOUR_ORDER_ID OR TRANSACTION_ID", refundRequest) 539 | if res != nil { 540 | // do something to `res` object 541 | } 542 | ``` 543 | #### Refund Transaction with Direct Refund 544 | ```go 545 | refundRequest := &coreapi.RefundReq{ 546 | RefundKey: "order1-ref1", 547 | Amount: 5000, 548 | Reason: "Item out of stock", 549 | } 550 | 551 | res, _ := c.DirectRefundTransaction("YOUR_ORDER_ID OR TRANSACTION_ID", refundRequest) 552 | if res != nil { 553 | // do something to `res` object 554 | } 555 | ``` 556 | ## 3. Advance Usage 557 | ### 3.1 Override Notification Url 558 | Merchant can opt to change or add custom notification urls on every transaction. It can be achieved by adding additional HTTP headers into charge request. 559 | For Midtrans Payment, there are two headers we provide: 560 | 561 | 1. `X-Append-Notification`: to add new notification url(s) alongside the settings on dashboard 562 | 2. `X-Override-Notification`: to use new notification url(s) disregarding the settings on dashboard 563 | Both header can only receive up to maximum of **3 urls**. 564 | 565 | > **Note:** When both `SetPaymentAppendNotif` and `SetPaymentOverrideNotif` are used together then only `OverrideNotif` will be used. 566 | 567 | #### 3.1.1 Set Override/Append notification globally 568 | ```go 569 | // Set override or append for globally 570 | midtrans.SetPaymentAppendNotification("YOUR-APPEND-NOTIFICATION-ENDPOINT") 571 | midtrans.SetPaymentOverrideNotification("YOUR-OVERRID-NOTIFICATION-ENDPOINT") 572 | ``` 573 | #### 3.1.2 Set Override/Append notification via client options 574 | ```go 575 | // 1. Initiate Gateway 576 | var c = coreapi.Client 577 | c.New("YOUR-SERVER-KEY", midtrans.Sandbox) 578 | 579 | // 2. Set Payment Override or Append via gateway options for specific request 580 | c.Options.SetPaymentAppendNotification("YOUR-APPEND-NOTIFICATION-ENDPOINT") 581 | c.Options.SetPaymentOverrideNotification("YOUR-APPEND-NOTIFICATION-ENDPOINT") 582 | 583 | // 3. Then request to Midtrans API 584 | res, _ := c.ChargeRequest("YOUR-REQUEST") 585 | ``` 586 | Please see our documentation for [the details](https://api-docs.midtrans.com/#override-notification-url) about the feature 587 | 588 | ### 3.2 Request using go Context 589 | With Gateway options object you can set Go Context for each request by the net/http machinery, and is available with `SetContext()` method. 590 | ```go 591 | c.Options.SetContext(context.Background()) 592 | ``` 593 | 594 | ### 3.3 Log Configuration 595 | By default in `Sandbox` the log level will use `LogDebug` level, that outputs informational messages for debugging. In `Production` this module will only logs the error messages (`LogError` level), that outputs error message to `os.stderr`. 596 | You have option to change the default log level configuration with global variable `midtrans.DefaultLoggerLevel`: 597 | ```go 598 | midtrans.DefaultLoggerLevel = &midtrans.LoggerImplementation{LogLevel: midtrans.LogDebug} 599 | 600 | // Details Log Level 601 | // NoLogging : sets a logger to not show the messages 602 | // LogError : sets a logger to show error messages only. 603 | // LogInfo : sets a logger to show information messages 604 | // LogDebug : sets a logger to show informational messages for debugging 605 | ``` 606 | 607 | ### 3.4 Override HTTP Client timeout 608 | By default, timeout value for HTTP Client 80 seconds. But you can override the HTTP client default config from global variable `midtrans.DefaultGoHttpClient`: 609 | ```go 610 | t := 300 * time.Millisecond 611 | midtrans.DefaultGoHttpClient = &http.Client{ 612 | Timeout: t, 613 | } 614 | ``` 615 | 616 | ## 4. Handling Error 617 | When using function that result in Midtrans API call e.g: c.ChargeTransaction(...) or s.CreateTransaction(...) there's a chance it may throw error (Midtrans [Error object](/error.go)), the error object will contains below properties that can be used as information to your error handling logic: 618 | ```go 619 | _, err = c.chargeTransaction(param) 620 | if err != nil { 621 | msg := err.Error() // general message error 622 | stsCode := err.GetStatusCode() // HTTP status code e.g: 400, 401, etc. 623 | rawApiRes := err.GetRawApiResponse() // raw Go HTTP response object 624 | rawErr := err.Unwrap() // raw Go err object 625 | } 626 | ``` 627 | midtrans.error complies with [Go standard error](https://go.dev/blog/go1.13-errors). which support `Error, Unwrap, Is, As`. 628 | ```go 629 | // sample using errors.As 630 | _, err := c.chargeTransaction(param) 631 | var Err *midtrans.Error 632 | if errors.As(err, &Err) { 633 | fmt.Println(Err.Message) 634 | fmt.Println(Err.StatusCode) 635 | } 636 | 637 | // sample using unwrap 638 | _, err := c.chargeTransaction(param) 639 | if err != nil { 640 | log.Print(errors.Unwrap(err)) 641 | fmt.Print(err) 642 | } 643 | ``` 644 | 645 | ## 5. Examples 646 | Examples are available on [/examples](example) folder 647 | There are: 648 | - [Core Api examples](example/simple/coreapi/sample.go) 649 | - [Snap examples](example/simple/snap/sample.go) 650 | - [Iris examples](example/simple/iris/sample.go) 651 | - [Readme Example](example/README.md) 652 | 653 | Integration test are available 654 | - [CoreApi Sample Functional Test](coreapi/client_test.go) 655 | - [Snap Sample Functional Test](snap/client_test.go) 656 | - [Iris Sample Functional Test](iris/client_test.go) 657 | 658 | 659 | ## Get help 660 | 661 | * [Midtrans Docs](https://docs.midtrans.com) 662 | * [Midtrans Dashboard ](https://dashboard.midtrans.com/) 663 | * [SNAP documentation](http://snap-docs.midtrans.com) 664 | * [Core API documentation](http://api-docs.midtrans.com) 665 | * Can't find answer you looking for? email to [support@midtrans.com](mailto:support@midtrans.com) 666 | -------------------------------------------------------------------------------- /bank.go: -------------------------------------------------------------------------------- 1 | package midtrans 2 | 3 | // Bank value 4 | type Bank string 5 | 6 | const ( 7 | //BankBni : bni 8 | BankBni Bank = "bni" 9 | 10 | //BankMandiri : mandiri 11 | BankMandiri Bank = "mandiri" 12 | 13 | //BankCimb : cimb 14 | BankCimb Bank = "cimb" 15 | 16 | //BankBca : bca 17 | BankBca Bank = "bca" 18 | 19 | //BankBri : bri 20 | BankBri Bank = "bri" 21 | 22 | //BankMaybank : maybank 23 | BankMaybank Bank = "maybank" 24 | 25 | //BankPermata : permata 26 | BankPermata Bank = "permata" 27 | 28 | //BankMega : mega 29 | BankMega Bank = "mega" 30 | ) 31 | -------------------------------------------------------------------------------- /coreapi/client.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/midtrans/midtrans-go" 11 | ) 12 | 13 | // Client : CoreAPI Client struct 14 | type Client struct { 15 | ServerKey string 16 | ClientKey string 17 | Env midtrans.EnvironmentType 18 | HttpClient midtrans.HttpClient 19 | Options *midtrans.ConfigOptions 20 | } 21 | 22 | // New : this function will always be called when the CoreApi is initiated 23 | func (c *Client) New(serverKey string, env midtrans.EnvironmentType) { 24 | c.Env = env 25 | c.ServerKey = serverKey 26 | c.Options = &midtrans.ConfigOptions{} 27 | c.HttpClient = midtrans.GetHttpClient(env) 28 | } 29 | 30 | // getDefaultClient : internal function to get default Client 31 | func getDefaultClient() Client { 32 | return Client{ 33 | ServerKey: midtrans.ServerKey, 34 | ClientKey: midtrans.ClientKey, 35 | Env: midtrans.Environment, 36 | HttpClient: midtrans.GetHttpClient(midtrans.Environment), 37 | Options: &midtrans.ConfigOptions{ 38 | PaymentOverrideNotification: midtrans.PaymentOverrideNotification, 39 | PaymentAppendNotification: midtrans.PaymentAppendNotification, 40 | }, 41 | } 42 | } 43 | 44 | // ChargeTransactionWithMap : Do `/charge` API request to Midtrans Core API return RAW MAP with Map as 45 | // body parameter, will be converted to JSON, more detail refer to: https://api-docs.midtrans.com 46 | func (c Client) ChargeTransactionWithMap(req *ChargeReqWithMap) (ResponseWithMap, *midtrans.Error) { 47 | resp := ResponseWithMap{} 48 | jsonReq, _ := json.Marshal(req) 49 | err := c.HttpClient.Call( 50 | http.MethodPost, 51 | fmt.Sprintf("%s/v2/charge", c.Env.BaseUrl()), 52 | &c.ServerKey, 53 | c.Options, 54 | bytes.NewBuffer(jsonReq), 55 | &resp) 56 | if err != nil { 57 | return resp, err 58 | } 59 | return resp, nil 60 | } 61 | 62 | // ChargeTransactionWithMap : Do `/charge` API request to Midtrans Core API return RAW MAP with Map as 63 | // body parameter, will be converted to JSON, more detail refer to: https://api-docs.midtrans.com 64 | func ChargeTransactionWithMap(req *ChargeReqWithMap) (ResponseWithMap, *midtrans.Error) { 65 | return getDefaultClient().ChargeTransactionWithMap(req) 66 | } 67 | 68 | // ChargeTransaction : Do `/charge` API request to Midtrans Core API return `coreapi.ChargeResponse` with `coreapi.ChargeReq` 69 | // as body parameter, will be converted to JSON, more detail refer to: https://api-docs.midtrans.com 70 | func (c Client) ChargeTransaction(req *ChargeReq) (*ChargeResponse, *midtrans.Error) { 71 | resp := &ChargeResponse{} 72 | jsonReq, _ := json.Marshal(req) 73 | err := c.HttpClient.Call(http.MethodPost, 74 | fmt.Sprintf("%s/v2/charge", c.Env.BaseUrl()), 75 | &c.ServerKey, 76 | c.Options, 77 | bytes.NewBuffer(jsonReq), 78 | resp, 79 | ) 80 | 81 | if err != nil { 82 | return resp, err 83 | } 84 | return resp, nil 85 | } 86 | 87 | // ChargeTransaction : Do `/charge` API request to Midtrans Core API return `coreapi.ChargeResponse` with `coreapi.ChargeReq` 88 | // as body parameter, will be converted to JSON, more detail refer to: https://api-docs.midtrans.com 89 | func ChargeTransaction(req *ChargeReq) (*ChargeResponse, *midtrans.Error) { 90 | return getDefaultClient().ChargeTransaction(req) 91 | } 92 | 93 | // CardToken : Do `/token` API request to Midtrans Core API return `coreapi.CardTokenResponse`, 94 | // more detail refer to: https://api-docs.midtrans.com/#get-token 95 | func (c Client) CardToken(cardNumber string, expMonth int, expYear int, cvv string, clientKey string) (*CardTokenResponse, *midtrans.Error) { 96 | resp := &CardTokenResponse{} 97 | URL := c.Env.BaseUrl() + 98 | "/v2/token?client_key=" + clientKey + 99 | "&card_number=" + cardNumber + 100 | "&card_exp_month=" + strconv.Itoa(expMonth) + 101 | "&card_exp_year=" + strconv.Itoa(expYear) + 102 | "&card_cvv=" + cvv 103 | err := c.HttpClient.Call(http.MethodGet, URL, nil, c.Options, nil, resp) 104 | 105 | if err != nil { 106 | return resp, err 107 | } 108 | return resp, nil 109 | } 110 | 111 | // CardToken : Do `/token` API request to Midtrans Core API return `coreapi.CardTokenResponse`, 112 | // more detail refer to: https://api-docs.midtrans.com/#get-token 113 | func CardToken(cardNumber string, expMonth int, expYear int, cvv string) (*CardTokenResponse, *midtrans.Error) { 114 | c := getDefaultClient() 115 | return c.CardToken(cardNumber, expMonth, expYear, cvv, c.ClientKey) 116 | } 117 | 118 | // RegisterCard : Do `/card/register` API request to Midtrans Core API return `coreapi.CardRegisterResponse`, 119 | // more detail refer to: https://api-docs.midtrans.com/#register-card 120 | func (c Client) RegisterCard(cardNumber string, expMonth int, expYear int, clientKey string) (*CardRegisterResponse, *midtrans.Error) { 121 | resp := &CardRegisterResponse{} 122 | URL := c.Env.BaseUrl() + 123 | "/v2/card/register?card_number=" + cardNumber + 124 | "&card_exp_month=" + strconv.Itoa(expMonth) + 125 | "&card_exp_year=" + strconv.Itoa(expYear) + 126 | "&client_key=" + clientKey 127 | 128 | err := c.HttpClient.Call(http.MethodGet, URL, nil, c.Options, nil, resp) 129 | 130 | if err != nil { 131 | return resp, err 132 | } 133 | return resp, nil 134 | } 135 | 136 | // RegisterCard : Do `/card/register` API request to Midtrans Core API return `coreapi.CardRegisterResponse`, 137 | // more detail refer to: https://api-docs.midtrans.com/#register-card 138 | func RegisterCard(cardNumber string, expMonth int, expYear int) (*CardRegisterResponse, *midtrans.Error) { 139 | c := getDefaultClient() 140 | return c.RegisterCard(cardNumber, expMonth, expYear, c.ClientKey) 141 | } 142 | 143 | // CardPointInquiry : Do `/point_inquiry/{tokenId}` API request to Midtrans Core API return `coreapi.CardTokenResponse`, 144 | // more detail refer to: https://api-docs.midtrans.com/#point-inquiry 145 | func (c Client) CardPointInquiry(cardToken string) (*PointInquiryResponse, *midtrans.Error) { 146 | resp := &PointInquiryResponse{} 147 | err := c.HttpClient.Call( 148 | http.MethodGet, 149 | fmt.Sprintf("%s/v2/point_inquiry/%s", c.Env.BaseUrl(), cardToken), 150 | &c.ServerKey, 151 | c.Options, 152 | nil, 153 | resp, 154 | ) 155 | 156 | if err != nil { 157 | return resp, err 158 | } 159 | return resp, nil 160 | } 161 | 162 | // CardPointInquiry : Do `/point_inquiry/{tokenId}` API request to Midtrans Core API return `coreapi.CardTokenResponse`, 163 | // more detail refer to: https://api-docs.midtrans.com/#point-inquiry 164 | func CardPointInquiry(cardToken string) (*PointInquiryResponse, *midtrans.Error) { 165 | return getDefaultClient().CardPointInquiry(cardToken) 166 | } 167 | 168 | // GetBIN : Do `v1/bins/{bin}` API request to Midtrans Core API return `coreapi.BinResponse`, 169 | // more detail refer to: https://api-docs.midtrans.com/#bin-api 170 | func (c Client) GetBIN(binNumber string) (*BinResponse, *midtrans.Error) { 171 | resp := &BinResponse{} 172 | err := c.HttpClient.Call( 173 | http.MethodGet, 174 | fmt.Sprintf("%s/v1/bins/%s", c.Env.BaseUrl(), binNumber), 175 | &c.ClientKey, 176 | c.Options, 177 | nil, 178 | resp, 179 | ) 180 | 181 | if err != nil { 182 | return resp, err 183 | } 184 | return resp, nil 185 | } 186 | 187 | // GetBIN : Do `/v1/bins/{bin}` API request to Midtrans Core API return `coreapi.BinResponse`, 188 | // more detail refer to: https://api-docs.midtrans.com/#bin-api 189 | func GetBIN(binNumber string) (*BinResponse, *midtrans.Error) { 190 | return getDefaultClient().GetBIN(binNumber) 191 | } 192 | -------------------------------------------------------------------------------- /coreapi/client_test.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/midtrans/midtrans-go" 6 | assert "github.com/stretchr/testify/require" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | const sandboxClientKey = "SB-Mid-client-yUgKb__vX_zH2TMN" 12 | const sandboxServerKey = "SB-Mid-server-TvgWB_Y9s81-rbMBH7zZ8BHW" 13 | const sampleCardNumber = "4811111111111114" 14 | const bniCardNumber = "4105058689481467" 15 | const bcaBinNumber = "45563300" 16 | 17 | func timestamp() string { 18 | return time.Now().UTC().Format("2006010215040105") 19 | } 20 | 21 | func getCardToken(cardNumber string) string { 22 | year := time.Now().Year() + 1 23 | midtrans.ClientKey = sandboxClientKey 24 | res, _ := CardToken(cardNumber, 12, year, "123") 25 | return res.TokenID 26 | } 27 | 28 | func createPayload(orderId string, paymentType CoreapiPaymentType, cardToken string) *ChargeReq { 29 | if paymentType == PaymentTypeCreditCard { 30 | return &ChargeReq{ 31 | PaymentType: paymentType, 32 | TransactionDetails: midtrans.TransactionDetails{ 33 | OrderID: orderId, 34 | GrossAmt: 10000, 35 | }, 36 | CreditCard: &CreditCardDetails{ 37 | TokenID: cardToken, 38 | }, 39 | } 40 | } 41 | return &ChargeReq{ 42 | PaymentType: paymentType, 43 | TransactionDetails: midtrans.TransactionDetails{ 44 | OrderID: orderId, 45 | GrossAmt: 10000, 46 | }, 47 | } 48 | } 49 | 50 | func TestRegisterCard(t *testing.T) { 51 | year := time.Now().Year() + 1 52 | midtrans.ClientKey = sandboxClientKey 53 | resp1, _ := RegisterCard(sampleCardNumber, 12, year) 54 | assert.Equal(t, resp1.StatusCode, "200") 55 | assert.Equal(t, resp1.MaskCard, "48111111-1114") 56 | 57 | c := Client{} 58 | c.New(sandboxServerKey, midtrans.Sandbox) 59 | resp2, _ := c.RegisterCard(bniCardNumber, 12, year, sandboxClientKey) 60 | assert.Equal(t, resp2.StatusCode, "200") 61 | assert.Equal(t, resp2.MaskCard, "41050586-1467") 62 | } 63 | 64 | func TestCardToken(t *testing.T) { 65 | year := time.Now().Year() + 1 66 | midtrans.ClientKey = sandboxClientKey 67 | resp1, _ := CardToken(sampleCardNumber, 12, year, "123") 68 | assert.Equal(t, resp1.StatusCode, "200") 69 | 70 | c := Client{} 71 | c.New(sandboxServerKey, midtrans.Sandbox) 72 | resp2, _ := c.CardToken(bniCardNumber, 12, year, "123", sandboxClientKey) 73 | assert.Equal(t, resp2.StatusCode, "200") 74 | } 75 | 76 | func TestChargeTransactionWithMap(t *testing.T) { 77 | req1 := &ChargeReqWithMap{ 78 | "payment_type": "gopay", 79 | "transaction_details": map[string]interface{}{ 80 | "order_id": "MID-GO-UNIT_TEST-3" + timestamp(), 81 | "gross_amount": 10000, 82 | }, 83 | } 84 | 85 | midtrans.ServerKey = sandboxServerKey 86 | resp, _ := ChargeTransactionWithMap(req1) 87 | assert.Equal(t, resp["status_code"], "201") 88 | assert.Equal(t, resp["payment_type"], "gopay") 89 | 90 | req2 := &ChargeReqWithMap{ 91 | "payment_type": PaymentTypeBankTransfer, 92 | "transaction_details": map[string]interface{}{ 93 | "order_id": "MID-GO-UNIT_TEST-4" + timestamp(), 94 | "gross_amount": 10000, 95 | }, 96 | } 97 | 98 | c := Client{} 99 | c.New(sandboxServerKey, midtrans.Sandbox) 100 | resp2, _ := c.ChargeTransactionWithMap(req2) 101 | assert.Equal(t, resp2["status_code"], "201") 102 | assert.Equal(t, resp2["payment_type"], "bank_transfer") 103 | } 104 | 105 | func TestChargeTransaction(t *testing.T) { 106 | midtrans.ServerKey = sandboxServerKey 107 | resp1, _ := ChargeTransaction(createPayload("MID-GO-UNIT_TEST-1"+timestamp(), PaymentTypeGopay, "")) 108 | assert.Equal(t, resp1.StatusCode, "201") 109 | assert.Equal(t, resp1.PaymentType, "gopay") 110 | 111 | c := Client{} 112 | c.New(sandboxServerKey, midtrans.Sandbox) 113 | resp2, _ := c.ChargeTransaction(createPayload("MID-GO-UNIT_TEST-2"+timestamp(), PaymentTypeGopay, "")) 114 | assert.Equal(t, resp2.StatusCode, "201") 115 | assert.Equal(t, resp2.PaymentType, "gopay") 116 | } 117 | 118 | func TestChargeTransactionWithIdempotencyKey(t *testing.T) { 119 | req := &ChargeReq{ 120 | PaymentType: PaymentTypeGopay, 121 | TransactionDetails: midtrans.TransactionDetails{ 122 | OrderID: "MID-GO-UNIT_TEST-" + timestamp(), 123 | GrossAmt: 10000, 124 | }, 125 | } 126 | 127 | c := Client{} 128 | c.New(sandboxServerKey, midtrans.Sandbox) 129 | c.Options.SetPaymentIdempotencyKey(timestamp()) 130 | 131 | resp1, _ := c.ChargeTransaction(req) 132 | resp2, _ := c.ChargeTransaction(req) 133 | 134 | assert.Equal(t, resp2, resp1) 135 | } 136 | 137 | func TestCardPointInquiry(t *testing.T) { 138 | midtrans.ServerKey = sandboxServerKey 139 | resp, _ := CardPointInquiry(getCardToken(bniCardNumber)) 140 | assert.Equal(t, resp.StatusCode, "200") 141 | } 142 | 143 | // Failure test case 144 | func TestRegisterCardFailure(t *testing.T) { 145 | midtrans.ClientKey = sandboxClientKey 146 | resp1, _ := RegisterCard(sampleCardNumber, 12, 2020) 147 | 148 | assert.Equal(t, resp1.StatusCode, "400") 149 | assert.Equal(t, resp1.StatusMessage, "One or more parameters in the payload is invalid.") 150 | 151 | c := Client{} 152 | c.New(sandboxServerKey, midtrans.Sandbox) 153 | resp2, _ := c.RegisterCard(bniCardNumber, 12, 2020, sandboxClientKey) 154 | assert.Equal(t, resp2.StatusCode, "400") 155 | assert.Equal(t, resp2.StatusMessage, "One or more parameters in the payload is invalid.") 156 | } 157 | 158 | func TestCardTokenFailure(t *testing.T) { 159 | midtrans.ClientKey = sandboxClientKey 160 | res, _ := CardToken(sampleCardNumber, 12, 2020, "123") 161 | 162 | assert.Equal(t, res.StatusCode, "400") 163 | assert.Equal(t, res.StatusMessage, "One or more parameters in the payload is invalid.") 164 | 165 | c := Client{} 166 | c.New(sandboxServerKey, midtrans.Sandbox) 167 | resp2, _ := c.CardToken(bniCardNumber, 12, 2020, "123", sandboxClientKey) 168 | assert.Equal(t, resp2.StatusCode, "400") 169 | assert.Equal(t, resp2.StatusMessage, "One or more parameters in the payload is invalid.") 170 | } 171 | 172 | func TestChargeTransactionNilParam(t *testing.T) { 173 | midtrans.ServerKey = sandboxServerKey 174 | _, err := ChargeTransaction(nil) 175 | assert.Equal(t, err.GetStatusCode(), 500) 176 | assert.Contains(t, err.GetMessage(), "Midtrans API is returning API error.") 177 | } 178 | 179 | func TestChargeTransactionWithMapNilParam(t *testing.T) { 180 | midtrans.ServerKey = sandboxServerKey 181 | _, err := ChargeTransactionWithMap(nil) 182 | assert.Equal(t, err.GetStatusCode(), 500) 183 | assert.Contains(t, err.GetMessage(), "Midtrans API is returning API error.") 184 | } 185 | 186 | func TestChargeWrongServerKey(t *testing.T) { 187 | midtrans.ServerKey = "DUMMY" 188 | _, err := ChargeTransaction(&ChargeReq{}) 189 | assert.Equal(t, err.GetStatusCode(), 401) 190 | 191 | c := Client{} 192 | c.New("DUMMY", midtrans.Sandbox) 193 | c.ChargeTransaction(&ChargeReq{}) 194 | assert.Equal(t, err.GetStatusCode(), 401) 195 | } 196 | 197 | func TestChargeTransactionWithQRISIncludesQRString(t *testing.T) { 198 | midtrans.ServerKey = sandboxServerKey 199 | resp1, _ := ChargeTransaction(createPayload("MID-GO-UNIT_TEST-1"+timestamp(), PaymentTypeQris, "")) 200 | assert.Equal(t, resp1.StatusCode, "201") 201 | assert.Equal(t, resp1.PaymentType, "qris") 202 | assert.NotEmpty(t, resp1.QRString) 203 | 204 | c := Client{} 205 | c.New(sandboxServerKey, midtrans.Sandbox) 206 | resp2, _ := c.ChargeTransaction(createPayload("MID-GO-UNIT_TEST-2"+timestamp(), PaymentTypeQris, "")) 207 | assert.Equal(t, resp2.StatusCode, "201") 208 | assert.Equal(t, resp2.PaymentType, "qris") 209 | assert.NotEmpty(t, resp2.QRString) 210 | } 211 | 212 | func TestGetBIN(t *testing.T) { 213 | midtrans.ClientKey = sandboxClientKey 214 | resp, _ := GetBIN(bcaBinNumber) 215 | assert.Equal(t, resp.Data.BankCode, "BCA") 216 | assert.Equal(t, resp.Data.RegistrationRequired, false) 217 | } 218 | 219 | type mockHTTPClient struct { 220 | // Define a field to hold the dummy response 221 | dummyResponseJSON []byte 222 | } 223 | 224 | // Implement the GetBIN method of the Client interface for the mock client 225 | func (m *mockHTTPClient) GetBIN(binNumber string) (*BinResponse, error) { 226 | // Return the stored dummy response 227 | var binResponse BinResponse 228 | err := json.Unmarshal(m.dummyResponseJSON, &binResponse) 229 | if err != nil { 230 | return nil, err 231 | } 232 | return &binResponse, nil 233 | } 234 | 235 | func TestGetBINWithRegistrationRequiredIsNullFromResponse(t *testing.T) { 236 | dummyResponseJSON := []byte(`{ 237 | "data": { 238 | "registration_required": null, 239 | "country_name": "INDONESIA", 240 | "country_code": "ID", 241 | "channel": "online_offline", 242 | "brand": "VISA", 243 | "bin_type": "CREDIT", 244 | "bin_class": "GOLD", 245 | "bin": "45563300", 246 | "bank_code": "BCA", 247 | "bank": "BANK CENTRAL ASIA" 248 | } 249 | }`) 250 | 251 | // Create an instance of the mock HTTP client with the dummy response 252 | mockClient := &mockHTTPClient{ 253 | dummyResponseJSON: dummyResponseJSON, 254 | } 255 | 256 | // Call the GetBIN function with the mock client 257 | resp, err := mockClient.GetBIN(bcaBinNumber) 258 | 259 | // Check if there's no error 260 | assert.NoError(t, err) 261 | 262 | // Check if the response matches the expected values 263 | assert.Equal(t, resp.Data.BankCode, "BCA") 264 | assert.Equal(t, resp.Data.RegistrationRequired, false) 265 | } 266 | 267 | func TestGetBINWithRegistrationRequiredIsTrueFromResponse(t *testing.T) { 268 | dummyResponseJSON := []byte(`{ 269 | "data": { 270 | "registration_required": true, 271 | "country_name": "INDONESIA", 272 | "country_code": "ID", 273 | "channel": "online_offline", 274 | "brand": "VISA", 275 | "bin_type": "CREDIT", 276 | "bin_class": "GOLD", 277 | "bin": "45563300", 278 | "bank_code": "BCA", 279 | "bank": "BANK CENTRAL ASIA" 280 | } 281 | }`) 282 | mockClient := &mockHTTPClient{ 283 | dummyResponseJSON: dummyResponseJSON, 284 | } 285 | resp, err := mockClient.GetBIN(bcaBinNumber) 286 | assert.NoError(t, err) 287 | assert.Equal(t, resp.Data.BankCode, "BCA") 288 | assert.Equal(t, resp.Data.RegistrationRequired, true) 289 | } 290 | -------------------------------------------------------------------------------- /coreapi/clientsubscription.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/midtrans/midtrans-go" 8 | "net/http" 9 | ) 10 | 11 | // CreateSubscription : Do `/v1/subscriptions` To create subscription that contains all details for creating transaction 12 | // more detail refer to: http://api-docs.midtrans.com/#recurring-api 13 | func (c Client) CreateSubscription(req *SubscriptionReq) (*CreateSubscriptionResponse, *midtrans.Error) { 14 | resp := &CreateSubscriptionResponse{} 15 | jsonReq, _ := json.Marshal(req) 16 | err := c.HttpClient.Call( 17 | http.MethodPost, 18 | fmt.Sprintf("%s/v1/subscriptions", c.Env.BaseUrl()), 19 | &c.ServerKey, 20 | c.Options, 21 | bytes.NewBuffer(jsonReq), 22 | resp) 23 | if err != nil { 24 | return resp, err 25 | } 26 | return resp, nil 27 | } 28 | 29 | // CreateSubscription : Do `/v1/subscriptions` To create subscription that contains all details for creating transaction 30 | // more detail refer to: http://api-docs.midtrans.com/#recurring-api 31 | func CreateSubscription(req *SubscriptionReq) (*CreateSubscriptionResponse, *midtrans.Error) { 32 | return getDefaultClient().CreateSubscription(req) 33 | } 34 | 35 | //GetSubscription : Do `/v1/subscriptions/{subscription_id}` To find subscription by id to see the subscription details 36 | // more detail refer to: http://api-docs.midtrans.com/#recurring-api 37 | func (c Client) GetSubscription(subscriptionId string) (*StatusSubscriptionResponse, *midtrans.Error) { 38 | resp := &StatusSubscriptionResponse{} 39 | err := c.HttpClient.Call( 40 | http.MethodGet, 41 | fmt.Sprintf("%s/v1/subscriptions/%s", c.Env.BaseUrl(), subscriptionId), 42 | &c.ServerKey, 43 | c.Options, 44 | nil, 45 | resp, 46 | ) 47 | 48 | if err != nil { 49 | return resp, err 50 | } 51 | return resp, nil 52 | } 53 | 54 | //GetSubscription : Do `/v1/subscriptions/{subscription_id}` To find subscription by id to see the subscription details 55 | // more detail refer to: http://api-docs.midtrans.com/#recurring-api 56 | func GetSubscription(subscriptionId string) (*StatusSubscriptionResponse, *midtrans.Error) { 57 | return getDefaultClient().GetSubscription(subscriptionId) 58 | } 59 | 60 | // DisableSubscription : Do `/v1/subscriptions/{subscription_id}/disable` To make the subscription inactive 61 | // (the subscription will not create transaction anymore) more detail refer to: http://api-docs.midtrans.com/#recurring-api 62 | func (c Client) DisableSubscription(subscriptionId string) (*DisableSubscriptionResponse, *midtrans.Error) { 63 | resp := &DisableSubscriptionResponse{} 64 | err := c.HttpClient.Call( 65 | http.MethodPost, 66 | fmt.Sprintf("%s/v1/subscriptions/%s/disable", c.Env.BaseUrl(), subscriptionId), 67 | &c.ServerKey, 68 | c.Options, 69 | nil, 70 | resp, 71 | ) 72 | 73 | if err != nil { 74 | return resp, err 75 | } 76 | return resp, nil 77 | } 78 | 79 | // DisableSubscription : Do `/v1/subscriptions/{subscription_id}/disable` To make the subscription inactive 80 | // (the subscription will not create transaction anymore) more detail refer to: http://api-docs.midtrans.com/#recurring-api 81 | func DisableSubscription(subscriptionId string) (*DisableSubscriptionResponse, *midtrans.Error) { 82 | return getDefaultClient().DisableSubscription(subscriptionId) 83 | } 84 | 85 | // EnableSubscription : Do `/v1/subscriptions/{subscription_id}/enable` To make the subscription active 86 | // (the subscription will create periodic transaction) more detail refer to: http://api-docs.midtrans.com/#recurring-api 87 | func (c Client) EnableSubscription(subscriptionId string) (*EnableSubscriptionResponse, *midtrans.Error) { 88 | resp := &EnableSubscriptionResponse{} 89 | err := c.HttpClient.Call( 90 | http.MethodPost, 91 | fmt.Sprintf("%s/v1/subscriptions/%s/enable", c.Env.BaseUrl(), subscriptionId), 92 | &c.ServerKey, 93 | c.Options, 94 | nil, 95 | resp, 96 | ) 97 | 98 | if err != nil { 99 | return resp, err 100 | } 101 | return resp, nil 102 | } 103 | 104 | // EnableSubscription : Do `/v1/subscriptions/{subscription_id}/enable` To make the subscription active 105 | // (the subscription will create periodic transaction) more detail refer to: http://api-docs.midtrans.com/#recurring-api 106 | func EnableSubscription(subscriptionId string) (*EnableSubscriptionResponse, *midtrans.Error) { 107 | return getDefaultClient().EnableSubscription(subscriptionId) 108 | } 109 | 110 | // UpdateSubscription : Do `/v1/subscriptions/{subscription_id}` To update existing subscription details 111 | // more detail refer to: http://api-docs.midtrans.com/#recurring-api 112 | func (c Client) UpdateSubscription(subscriptionId string, req *SubscriptionReq) (*UpdateSubscriptionResponse, *midtrans.Error) { 113 | resp := &UpdateSubscriptionResponse{} 114 | jsonReq, _ := json.Marshal(req) 115 | err := c.HttpClient.Call( 116 | http.MethodPatch, 117 | fmt.Sprintf("%s/v1/subscriptions/%s", c.Env.BaseUrl(), subscriptionId), 118 | &c.ServerKey, 119 | c.Options, 120 | bytes.NewBuffer(jsonReq), 121 | resp) 122 | if err != nil { 123 | return resp, err 124 | } 125 | return resp, nil 126 | } 127 | 128 | // UpdateSubscription : Do `/v1/subscriptions/{subscription_id}` To update existing subscription details 129 | // more detail refer to: http://api-docs.midtrans.com/#recurring-api 130 | func UpdateSubscription(subscriptionId string, req *SubscriptionReq) (*UpdateSubscriptionResponse, *midtrans.Error) { 131 | return getDefaultClient().UpdateSubscription(subscriptionId, req) 132 | } 133 | -------------------------------------------------------------------------------- /coreapi/clientsubscription_test.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "github.com/midtrans/midtrans-go" 5 | assert "github.com/stretchr/testify/require" 6 | "log" 7 | "testing" 8 | ) 9 | 10 | /* 11 | This is subscription API integration test section 12 | */ 13 | var subs Client 14 | 15 | var subscriptionId string 16 | var subscriptionName string 17 | 18 | func initiateMidtransSubs() { 19 | midtrans.ServerKey = sandboxServerKey 20 | midtrans.ClientKey = sandboxClientKey 21 | 22 | subs.New(sandboxServerKey, midtrans.Sandbox) 23 | } 24 | 25 | func TestCreateSubscription(t *testing.T) { 26 | initiateMidtransSubs() 27 | subscriptionName = "MidGoSubTest-" + timestamp() 28 | req := &SubscriptionReq{ 29 | Name: subscriptionName, 30 | Amount: 100000, 31 | Currency: "IDR", 32 | PaymentType: PaymentTypeCreditCard, 33 | Token: "DUMMY", 34 | Schedule: ScheduleDetails{ 35 | Interval: 1, 36 | IntervalUnit: "month", 37 | MaxInterval: 12, 38 | }, 39 | CustomerDetails: &midtrans.CustomerDetails{ 40 | FName: "MidtransGo", 41 | LName: "SubscriptionTest", 42 | Email: "mid-go@mainlesia.com", 43 | Phone: "081234567", 44 | }, 45 | } 46 | 47 | resp, err := subs.CreateSubscription(req) 48 | if err != nil { 49 | log.Println("Failure :") 50 | log.Fatalln(err) 51 | } else { 52 | log.Println("Success :") 53 | log.Println(resp) 54 | assert.Equal(t, resp.Status, "active") 55 | assert.NotEmpty(t, resp.ID) 56 | subscriptionId = resp.ID 57 | } 58 | 59 | } 60 | 61 | func TestGetSubscription(t *testing.T) { 62 | initiateMidtransSubs() 63 | resp, err := subs.GetSubscription(subscriptionId) 64 | if err != nil { 65 | log.Println("Failure :") 66 | log.Fatal(err) 67 | } else { 68 | log.Println("Success :") 69 | log.Println(resp) 70 | assert.Equal(t, resp.Status, "active") 71 | assert.Equal(t, resp.StatusMessage, "") 72 | } 73 | } 74 | 75 | func TestDisableSubscription(t *testing.T) { 76 | initiateMidtransSubs() 77 | resp, err := subs.DisableSubscription(subscriptionId) 78 | if err != nil { 79 | log.Println("Failure :") 80 | log.Fatal(err) 81 | } else { 82 | log.Println("Success :") 83 | log.Println(resp) 84 | assert.Equal(t, resp.StatusMessage, "Subscription is updated.") 85 | } 86 | } 87 | 88 | func TestEnableSubscription(t *testing.T) { 89 | initiateMidtransSubs() 90 | resp, err := subs.EnableSubscription(subscriptionId) 91 | if err != nil { 92 | log.Println("Failure :") 93 | log.Fatal(err) 94 | } else { 95 | log.Println("Success :") 96 | log.Println(resp) 97 | assert.Equal(t, resp.StatusMessage, "Subscription is updated.") 98 | } 99 | } 100 | 101 | func TestUpdateSubscription(t *testing.T) { 102 | initiateMidtransSubs() 103 | reqUpdate := &SubscriptionReq{ 104 | Name: subscriptionName, 105 | Amount: 50000, 106 | Currency: "IDR", 107 | PaymentType: PaymentTypeCreditCard, 108 | Token: "DUMMY", 109 | Schedule: ScheduleDetails{ 110 | Interval: 1, 111 | IntervalUnit: "month", 112 | MaxInterval: 12, 113 | }, 114 | CustomerDetails: &midtrans.CustomerDetails{ 115 | FName: "MidtransGo", 116 | LName: "SubscriptionTest", 117 | Email: "mid-go@mainlesia.com", 118 | Phone: "081234567", 119 | }, 120 | } 121 | resp, err := subs.UpdateSubscription(subscriptionId, reqUpdate) 122 | if err != nil { 123 | log.Println("Failure :") 124 | log.Fatal(err) 125 | } else { 126 | log.Println("Success :") 127 | log.Println(resp) 128 | assert.Equal(t, resp.StatusMessage, "Subscription is updated.") 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /coreapi/clienttokenization.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/midtrans/midtrans-go" 8 | "net/http" 9 | ) 10 | 11 | // LinkPaymentAccount : Do `/v2/pay/account` to link customer account to be used for specific payment channels 12 | // more detail refer to: https://api-docs.midtrans.com/#create-pay-account 13 | func (c Client) LinkPaymentAccount(req *PaymentAccountReq) (*PaymentAccountResponse, *midtrans.Error) { 14 | resp := &PaymentAccountResponse{} 15 | jsonReq, _ := json.Marshal(req) 16 | err := c.HttpClient.Call( 17 | http.MethodPost, 18 | fmt.Sprintf("%s/v2/pay/account", c.Env.BaseUrl()), 19 | &c.ServerKey, 20 | c.Options, 21 | bytes.NewBuffer(jsonReq), 22 | resp) 23 | if err != nil { 24 | return resp, err 25 | } 26 | return resp, nil 27 | } 28 | 29 | // LinkPaymentAccount : Do `/v2/pay/account/{account_id}` to link customer account to be used for specific payment channels 30 | // more detail refer to: https://api-docs.midtrans.com/#get-pay-account 31 | func LinkPaymentAccount(req *PaymentAccountReq) (*PaymentAccountResponse, *midtrans.Error) { 32 | return getDefaultClient().LinkPaymentAccount(req) 33 | } 34 | 35 | // GetPaymentAccount : Do `/v2/pay/account/{account_id}t` to get customer payment account details 36 | // more detail refer to: https://api-docs.midtrans.com/#get-pay-account 37 | func (c Client) GetPaymentAccount(accountId string) (*PaymentAccountResponse, *midtrans.Error) { 38 | resp := &PaymentAccountResponse{} 39 | err := c.HttpClient.Call( 40 | http.MethodGet, 41 | fmt.Sprintf("%s/v2/pay/account/%s", c.Env.BaseUrl(), accountId), 42 | &c.ServerKey, 43 | c.Options, 44 | nil, 45 | resp) 46 | if err != nil { 47 | return resp, err 48 | } 49 | return resp, nil 50 | } 51 | 52 | // GetPaymentAccount : Do `/v2/pay/account/{account_id}` to get customer payment account details 53 | // more detail refer to: https://api-docs.midtrans.com/#get-pay-account 54 | func GetPaymentAccount(accountId string) (*PaymentAccountResponse, *midtrans.Error) { 55 | return getDefaultClient().GetPaymentAccount(accountId) 56 | } 57 | 58 | // UnlinkPaymentAccount : Do `/v2/pay/account/{account_id}/unbind` to unbind a linked customer account 59 | // more detail refer to: https://api-docs.midtrans.com/#unbind-pay-account 60 | func (c Client) UnlinkPaymentAccount(accountId string) (*PaymentAccountResponse, *midtrans.Error) { 61 | resp := &PaymentAccountResponse{} 62 | err := c.HttpClient.Call( 63 | http.MethodPost, 64 | fmt.Sprintf("%s/v2/pay/account/%s/unbind", c.Env.BaseUrl(), accountId), 65 | &c.ServerKey, 66 | c.Options, 67 | nil, 68 | resp) 69 | if err != nil { 70 | return resp, err 71 | } 72 | return resp, nil 73 | } 74 | 75 | // UnlinkPaymentAccount : Do `/v2/pay/account/{account_id}/unbind` to unbind a linked customer account 76 | // more detail refer to: https://api-docs.midtrans.com/#unbind-pay-account 77 | func UnlinkPaymentAccount(accountId string) (*PaymentAccountResponse, *midtrans.Error) { 78 | return getDefaultClient().UnlinkPaymentAccount(accountId) 79 | } 80 | -------------------------------------------------------------------------------- /coreapi/clienttokenization_test.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "github.com/midtrans/midtrans-go" 5 | assert "github.com/stretchr/testify/require" 6 | "log" 7 | "testing" 8 | ) 9 | 10 | /* 11 | This is Tokenization API integration test 12 | */ 13 | var coreapi Client 14 | var accountId string 15 | var phoneNotRegistered = "123450001" 16 | var phoneNumberBlocked = "123450002" 17 | 18 | func initiateMidtransTokenization() { 19 | midtrans.ServerKey = sandboxServerKey 20 | midtrans.ClientKey = sandboxClientKey 21 | 22 | subs.New(sandboxServerKey, midtrans.Sandbox) 23 | } 24 | 25 | func paymentAccount(phoneNumber string) *PaymentAccountReq { 26 | return &PaymentAccountReq{ 27 | PaymentType: PaymentTypeGopay, 28 | GopayPartner: &GopayPartnerDetails{ 29 | PhoneNumber: phoneNumber, 30 | CountryCode: "62", 31 | RedirectURL: "https://midtrans.com/", 32 | }, 33 | } 34 | } 35 | 36 | func TestLinkPaymentAccountUserNotFound(t *testing.T) { 37 | initiateMidtransTokenization() 38 | req := paymentAccount(phoneNotRegistered) 39 | 40 | resp, err := subs.LinkPaymentAccount(req) 41 | if err != nil { 42 | log.Println("Failure :") 43 | log.Fatalln(err) 44 | } else { 45 | log.Println("Success :") 46 | log.Println(resp) 47 | assert.Equal(t, "202", resp.StatusCode) 48 | assert.Equal(t, "User Not Found", resp.ChannelResponseMessage) 49 | } 50 | } 51 | 52 | func TestLinkPaymentAccountUserBlocked(t *testing.T) { 53 | initiateMidtransTokenization() 54 | req := paymentAccount(phoneNumberBlocked) 55 | 56 | resp, err := subs.LinkPaymentAccount(req) 57 | if err != nil { 58 | log.Println("Failure :") 59 | log.Fatalln(err) 60 | } else { 61 | log.Println("Success :") 62 | log.Println(resp) 63 | assert.Equal(t, "202", resp.StatusCode) 64 | assert.Equal(t, "Wallet is Blocked", resp.ChannelResponseMessage) 65 | } 66 | } 67 | 68 | func TestLinkPaymentAccount(t *testing.T) { 69 | initiateMidtransTokenization() 70 | req := paymentAccount("628123456789") 71 | 72 | resp, err := subs.LinkPaymentAccount(req) 73 | if err != nil { 74 | log.Println("Failure :") 75 | log.Fatalln(err) 76 | } else { 77 | log.Println("Success :") 78 | log.Println(resp) 79 | assert.Equal(t, "201", resp.StatusCode) 80 | assert.NotEmpty(t, resp.AccountId) 81 | assert.NotEmpty(t, resp.Actions) 82 | accountId = resp.AccountId 83 | } 84 | } 85 | 86 | func TestGetPaymentAccount(t *testing.T) { 87 | initiateMidtransTokenization() 88 | 89 | resp, err := subs.GetPaymentAccount(accountId) 90 | if err != nil { 91 | log.Println("Failure :") 92 | log.Fatalln(err) 93 | } else { 94 | log.Println("Success :") 95 | log.Println(resp) 96 | assert.Equal(t, "201", resp.StatusCode) 97 | assert.Equal(t, accountId, resp.AccountId) 98 | } 99 | } 100 | 101 | func TestUnlinkPaymentAccount(t *testing.T) { 102 | initiateMidtransTokenization() 103 | _, err := subs.UnlinkPaymentAccount(accountId) 104 | if err != nil { 105 | log.Println("Failure :", err) 106 | assert.Equal(t, 412, err.StatusCode) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /coreapi/clienttransaction.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/midtrans/midtrans-go" 8 | "net/http" 9 | ) 10 | 11 | // CheckTransaction : Do `/{orderId}/status` API request to Midtrans Core API return `coreapi.TransactionStatusResponse`, 12 | // more detail refer to: https://api-docs.midtrans.com/#get-transaction-status 13 | func (c Client) CheckTransaction(param string) (*TransactionStatusResponse, *midtrans.Error) { 14 | resp := &TransactionStatusResponse{} 15 | err := c.HttpClient.Call( 16 | http.MethodGet, 17 | fmt.Sprintf("%s/v2/%s/status", c.Env.BaseUrl(), param), 18 | &c.ServerKey, 19 | nil, 20 | nil, 21 | resp, 22 | ) 23 | 24 | if err != nil { 25 | return resp, err 26 | } 27 | return resp, nil 28 | } 29 | 30 | // CheckTransaction : Do `/{orderId}/status` API request to Midtrans Core API return `coreapi.TransactionStatusResponse`, 31 | // more detail refer to: https://api-docs.midtrans.com/#get-transaction-status 32 | func CheckTransaction(param string) (*TransactionStatusResponse, *midtrans.Error) { 33 | return getDefaultClient().CheckTransaction(param) 34 | } 35 | 36 | // ApproveTransaction : Do `/{orderId}/approve` API request to Midtrans Core API return `coreapi.ApproveResponse`, 37 | // more detail refer to: https://api-docs.midtrans.com/#approve-transaction 38 | func (c Client) ApproveTransaction(param string) (*ApproveResponse, *midtrans.Error) { 39 | resp := &ApproveResponse{} 40 | err := c.HttpClient.Call( 41 | http.MethodPost, 42 | fmt.Sprintf("%s/v2/%s/approve", c.Env.BaseUrl(), param), 43 | &c.ServerKey, 44 | c.Options, 45 | nil, 46 | resp, 47 | ) 48 | 49 | if err != nil { 50 | return resp, err 51 | } 52 | return resp, nil 53 | } 54 | 55 | // ApproveTransaction : Do `/{orderId}/approve` API request to Midtrans Core API return `coreapi.ApproveResponse`, 56 | // more detail refer to: https://api-docs.midtrans.com/#approve-transaction 57 | func ApproveTransaction(param string) (*ApproveResponse, *midtrans.Error) { 58 | return getDefaultClient().ApproveTransaction(param) 59 | } 60 | 61 | // DenyTransaction : Do `/{orderId}/deny` API request to Midtrans Core API return `coreapi.DenyResponse`, 62 | // more detail refer to: https://api-docs.midtrans.com/#deny-transaction 63 | func (c Client) DenyTransaction(param string) (*DenyResponse, *midtrans.Error) { 64 | resp := &DenyResponse{} 65 | err := c.HttpClient.Call( 66 | http.MethodPost, 67 | fmt.Sprintf("%s/v2/%s/deny", c.Env.BaseUrl(), param), 68 | &c.ServerKey, 69 | c.Options, 70 | nil, 71 | resp, 72 | ) 73 | 74 | if err != nil { 75 | return resp, err 76 | } 77 | return resp, nil 78 | } 79 | 80 | // DenyTransaction : Do `/{orderId}/deny` API request to Midtrans Core API return `coreapi.DenyResponse`, 81 | // more detail refer to: https://api-docs.midtrans.com/#deny-transaction 82 | func DenyTransaction(param string) (*DenyResponse, *midtrans.Error) { 83 | return getDefaultClient().DenyTransaction(param) 84 | } 85 | 86 | // CancelTransaction : Do `/{orderId}/cancel` API request to Midtrans Core API return `coreapi.CancelResponse`, 87 | // more detail refer to: https://api-docs.midtrans.com/#cancel-transaction 88 | func (c Client) CancelTransaction(param string) (*CancelResponse, *midtrans.Error) { 89 | resp := &CancelResponse{} 90 | err := c.HttpClient.Call( 91 | http.MethodPost, 92 | fmt.Sprintf("%s/v2/%s/cancel", c.Env.BaseUrl(), param), 93 | &c.ServerKey, 94 | c.Options, 95 | nil, 96 | resp, 97 | ) 98 | 99 | if err != nil { 100 | return resp, err 101 | } 102 | return resp, nil 103 | } 104 | 105 | // CancelTransaction : Do `/{orderId}/cancel` API request to Midtrans Core API return `coreapi.CancelResponse`, 106 | // more detail refer to: https://api-docs.midtrans.com/#cancel-transaction 107 | func CancelTransaction(param string) (*CancelResponse, *midtrans.Error) { 108 | return getDefaultClient().CancelTransaction(param) 109 | } 110 | 111 | // ExpireTransaction : Do `/{orderId}/expire` API request to Midtrans Core API return `coreapi.ExpireResponse`, 112 | // more detail refer to: https://api-docs.midtrans.com/#expire-transaction 113 | func (c Client) ExpireTransaction(param string) (*ExpireResponse, *midtrans.Error) { 114 | resp := &ExpireResponse{} 115 | err := c.HttpClient.Call( 116 | http.MethodPost, 117 | fmt.Sprintf("%s/v2/%s/expire", c.Env.BaseUrl(), param), 118 | &c.ServerKey, 119 | c.Options, 120 | nil, 121 | resp, 122 | ) 123 | 124 | if err != nil { 125 | return resp, err 126 | } 127 | return resp, nil 128 | } 129 | 130 | // ExpireTransaction : Do `/{orderId}/expire` API request to Midtrans Core API return `coreapi.ExpireResponse`, 131 | // more detail refer to: https://api-docs.midtrans.com/#expire-transaction 132 | func ExpireTransaction(param string) (*ExpireResponse, *midtrans.Error) { 133 | return getDefaultClient().ExpireTransaction(param) 134 | } 135 | 136 | // RefundTransaction : Do `/{orderId}/refund` API request to Midtrans Core API return `coreapi.RefundResponse`, 137 | // with `coreapi.RefundReq` as body parameter, will be converted to JSON, 138 | // more detail refer to: https://api-docs.midtrans.com/#refund-transaction 139 | func (c Client) RefundTransaction(param string, req *RefundReq) (*RefundResponse, *midtrans.Error) { 140 | resp := &RefundResponse{} 141 | jsonReq, _ := json.Marshal(req) 142 | err := c.HttpClient.Call( 143 | http.MethodPost, 144 | fmt.Sprintf("%s/v2/%s/refund", c.Env.BaseUrl(), param), 145 | &c.ServerKey, 146 | c.Options, 147 | bytes.NewBuffer(jsonReq), 148 | resp, 149 | ) 150 | 151 | if err != nil { 152 | return resp, err 153 | } 154 | return resp, nil 155 | } 156 | 157 | // RefundTransaction : Do `/{orderId}/refund` API request to Midtrans Core API return `coreapi.RefundResponse`, 158 | // with `coreapi.RefundReq` as body parameter, will be converted to JSON, 159 | // more detail refer to: https://api-docs.midtrans.com/#refund-transaction 160 | func RefundTransaction(param string, req *RefundReq) (*RefundResponse, *midtrans.Error) { 161 | return getDefaultClient().RefundTransaction(param, req) 162 | } 163 | 164 | // DirectRefundTransaction : Do `/{orderId}/refund/online/direct` API request to Midtrans Core API return `coreapi.RefundResponse`, 165 | // with `coreapi.CaptureReq` as body parameter, will be converted to JSON, 166 | // more detail refer to: https://api-docs.midtrans.com/#direct-refund-transaction 167 | func (c Client) DirectRefundTransaction(param string, req *RefundReq) (*RefundResponse, *midtrans.Error) { 168 | resp := &RefundResponse{} 169 | jsonReq, _ := json.Marshal(req) 170 | err := c.HttpClient.Call( 171 | http.MethodPost, 172 | fmt.Sprintf("%s/v2/%s/refund/online/direct", c.Env.BaseUrl(), param), 173 | &c.ServerKey, 174 | c.Options, 175 | bytes.NewBuffer(jsonReq), 176 | resp, 177 | ) 178 | 179 | if err != nil { 180 | return resp, err 181 | } 182 | return resp, nil 183 | } 184 | 185 | // DirectRefundTransaction : Do `/{orderId}/refund/online/direct` API request to Midtrans Core API return `coreapi.RefundResponse`, 186 | // with `coreapi.RefundReq` as body parameter, will be converted to JSON, 187 | // more detail refer to: https://api-docs.midtrans.com/#direct-refund-transaction 188 | func DirectRefundTransaction(param string, req *RefundReq) (*RefundResponse, *midtrans.Error) { 189 | return getDefaultClient().DirectRefundTransaction(param, req) 190 | } 191 | 192 | // CaptureTransaction : Do `/{orderId}/capture` API request to Midtrans Core API return `coreapi.CaptureResponse`, 193 | // with `coreapi.CaptureReq` as body parameter, will be converted to JSON, 194 | // more detail refer to: https://api-docs.midtrans.com/#capture-transaction 195 | func (c Client) CaptureTransaction(req *CaptureReq) (*CaptureResponse, *midtrans.Error) { 196 | resp := &CaptureResponse{} 197 | jsonReq, _ := json.Marshal(req) 198 | err := c.HttpClient.Call( 199 | http.MethodPost, 200 | fmt.Sprintf("%s/v2/capture", c.Env.BaseUrl()), 201 | &c.ServerKey, 202 | c.Options, 203 | bytes.NewBuffer(jsonReq), 204 | resp, 205 | ) 206 | 207 | if err != nil { 208 | return resp, err 209 | } 210 | return resp, nil 211 | } 212 | 213 | // CaptureTransaction : Do `/{orderId}/capture` API request to Midtrans Core API return `coreapi.CaptureResponse`, 214 | // with `coreapi.CaptureReq` as body parameter, will be converted to JSON, 215 | // more detail refer to: https://api-docs.midtrans.com/#capture-transaction 216 | func CaptureTransaction(req *CaptureReq) (*CaptureResponse, *midtrans.Error) { 217 | return getDefaultClient().CaptureTransaction(req) 218 | } 219 | 220 | // GetStatusB2B : Do `/{orderId}/status/b2b` API request to Midtrans Core API return `coreapi.TransactionStatusB2bResponse`, 221 | // more detail refer to: https://api-docs.midtrans.com/#get-transaction-status-b2b 222 | func (c Client) GetStatusB2B(param string) (*TransactionStatusB2bResponse, *midtrans.Error) { 223 | resp := &TransactionStatusB2bResponse{} 224 | err := c.HttpClient.Call( 225 | http.MethodGet, 226 | fmt.Sprintf("%s/v2/%s/status/b2b", c.Env.BaseUrl(), param), 227 | &c.ServerKey, 228 | c.Options, 229 | nil, 230 | resp, 231 | ) 232 | 233 | if err != nil { 234 | return resp, err 235 | } 236 | return resp, nil 237 | } 238 | 239 | // GetStatusB2B : Do `/{orderId}/status/b2b` API request to Midtrans Core API return `coreapi.TransactionStatusB2bResponse`, 240 | // more detail refer to: https://api-docs.midtrans.com/#get-transaction-status-b2b 241 | func GetStatusB2B(param string) (*TransactionStatusB2bResponse, *midtrans.Error) { 242 | return getDefaultClient().GetStatusB2B(param) 243 | } 244 | -------------------------------------------------------------------------------- /coreapi/clienttransaction_test.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "github.com/midtrans/midtrans-go" 5 | assert "github.com/stretchr/testify/require" 6 | "testing" 7 | ) 8 | 9 | func TestCheckTransaction(t *testing.T) { 10 | midtrans.ServerKey = sandboxServerKey 11 | _, err := CheckTransaction("DUMMY") 12 | assert.Equal(t, err.GetStatusCode(), 404) 13 | 14 | c := Client{} 15 | c.New(sandboxServerKey, midtrans.Sandbox) 16 | _, err2 := c.CheckTransaction("DUMMY") 17 | assert.Equal(t, err2.StatusCode, 404) 18 | } 19 | 20 | func TestApproveTransaction(t *testing.T) { 21 | midtrans.ServerKey = sandboxServerKey 22 | _, err := ApproveTransaction("DUMMY") 23 | assert.Equal(t, err.GetStatusCode(), 404) 24 | 25 | c := Client{} 26 | c.New(sandboxServerKey, midtrans.Sandbox) 27 | _, err2 := c.ApproveTransaction("DUMMY") 28 | assert.Equal(t, err2.StatusCode, 404) 29 | } 30 | 31 | func TestDenyTransaction(t *testing.T) { 32 | midtrans.ServerKey = sandboxServerKey 33 | _, err := DenyTransaction("DUMMY") 34 | assert.Equal(t, err.GetStatusCode(), 404) 35 | 36 | c := Client{} 37 | c.New(sandboxServerKey, midtrans.Sandbox) 38 | _, err2 := c.DenyTransaction("DUMMY") 39 | assert.Equal(t, err2.StatusCode, 404) 40 | } 41 | 42 | func TestCancelTransaction(t *testing.T) { 43 | midtrans.ServerKey = sandboxServerKey 44 | _, err := CancelTransaction("DUMMY") 45 | assert.Equal(t, err.GetStatusCode(), 404) 46 | 47 | c := Client{} 48 | c.New(sandboxServerKey, midtrans.Sandbox) 49 | _, err2 := c.CancelTransaction("DUMMY") 50 | assert.Equal(t, err2.StatusCode, 404) 51 | } 52 | 53 | func TestExpireTransaction(t *testing.T) { 54 | midtrans.ServerKey = sandboxServerKey 55 | _, err := ExpireTransaction("DUMMY") 56 | assert.Equal(t, err.GetStatusCode(), 404) 57 | 58 | c := Client{} 59 | c.New(sandboxServerKey, midtrans.Sandbox) 60 | _, err2 := c.ExpireTransaction("DUMMY") 61 | assert.Equal(t, err2.StatusCode, 404) 62 | } 63 | 64 | func TestRefundTransaction(t *testing.T) { 65 | refundReq := &RefundReq{ 66 | Amount: 10000, 67 | Reason: "Out of stock", 68 | } 69 | midtrans.ServerKey = sandboxServerKey 70 | _, err1 := RefundTransaction("DUMMY", refundReq) 71 | assert.Equal(t, err1.StatusCode, 404) 72 | 73 | c := Client{} 74 | c.New(sandboxServerKey, midtrans.Sandbox) 75 | _, err2 := c.RefundTransaction("DUMMY", refundReq) 76 | assert.Equal(t, err2.StatusCode, 404) 77 | } 78 | 79 | func TestDirectRefundTransaction(t *testing.T) { 80 | refundReq := &RefundReq{ 81 | RefundKey: "ORDER-ID-UNIQUE-ID", 82 | Amount: 10000, 83 | Reason: "Out of stock", 84 | } 85 | midtrans.ServerKey = sandboxServerKey 86 | _, err1 := DirectRefundTransaction("DUMMY", refundReq) 87 | assert.NotNil(t, err1) 88 | 89 | c := Client{} 90 | c.New(sandboxServerKey, midtrans.Sandbox) 91 | _, err2 := c.DirectRefundTransaction("DUMMY", refundReq) 92 | assert.NotNil(t, err2) 93 | } 94 | 95 | func TestCaptureTransaction(t *testing.T) { 96 | reqCapture := &CaptureReq{ 97 | TransactionID: "DUMMY", 98 | GrossAmt: 10000, 99 | } 100 | midtrans.ServerKey = sandboxServerKey 101 | _, err := CaptureTransaction(reqCapture) 102 | assert.Equal(t, err.GetStatusCode(), 404) 103 | 104 | c := Client{} 105 | c.New(sandboxServerKey, midtrans.Sandbox) 106 | _, err2 := c.CaptureTransaction(reqCapture) 107 | assert.Equal(t, err2.StatusCode, 404) 108 | } 109 | 110 | func TestGetStatusB2B(t *testing.T) { 111 | midtrans.ServerKey = sandboxServerKey 112 | _, err1 := GetStatusB2B("DUMMY") 113 | assert.Equal(t, err1.StatusCode, 404) 114 | 115 | c := Client{} 116 | c.New(sandboxServerKey, midtrans.Sandbox) 117 | _, err2 := GetStatusB2B("DUMMY") 118 | assert.Equal(t, err2.StatusCode, 404) 119 | } 120 | -------------------------------------------------------------------------------- /coreapi/paymenttype.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | type CoreapiPaymentType string 4 | type SubscriptionPaymentType = CoreapiPaymentType 5 | 6 | const ( 7 | // PaymentTypeBankTransfer : bank_transfer 8 | PaymentTypeBankTransfer CoreapiPaymentType = "bank_transfer" 9 | 10 | // PaymentTypeGopay : gopay 11 | PaymentTypeGopay CoreapiPaymentType = "gopay" 12 | 13 | // PaymentTypeShopeepay : shopeepay 14 | PaymentTypeShopeepay CoreapiPaymentType = "shopeepay" 15 | 16 | // PaymentTypeQris : qris 17 | PaymentTypeQris CoreapiPaymentType = "qris" 18 | 19 | // PaymentTypeCreditCard : credit_card 20 | PaymentTypeCreditCard CoreapiPaymentType = "credit_card" 21 | 22 | // PaymentTypeEChannel : echannel 23 | PaymentTypeEChannel CoreapiPaymentType = "echannel" 24 | 25 | // PaymentTypeBCAKlikpay : bca_klikpay 26 | PaymentTypeBCAKlikpay CoreapiPaymentType = "bca_klikpay" 27 | 28 | // PaymentTypeKlikBca : bca_klikbca 29 | PaymentTypeKlikBca CoreapiPaymentType = "bca_klikbca" 30 | 31 | // PaymentTypeBRIEpay : bri_epay 32 | PaymentTypeBRIEpay CoreapiPaymentType = "bri_epay" 33 | 34 | // PaymentTypeCimbClicks : cimb_clicks 35 | PaymentTypeCimbClicks CoreapiPaymentType = "cimb_clicks" 36 | 37 | // PaymentTypeDanamonOnline : danamon_online 38 | PaymentTypeDanamonOnline CoreapiPaymentType = "danamon_online" 39 | 40 | // PaymentTypeConvenienceStore : cstore 41 | PaymentTypeConvenienceStore CoreapiPaymentType = "cstore" 42 | 43 | // PaymentTypeAkulaku : akulaku 44 | PaymentTypeAkulaku CoreapiPaymentType = "akulaku" 45 | 46 | // PaymentTypeMandiriClickpay : mandiri_clickpay 47 | PaymentTypeMandiriClickpay CoreapiPaymentType = "mandiri_clickpay" 48 | ) 49 | -------------------------------------------------------------------------------- /coreapi/request.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import ( 4 | "github.com/midtrans/midtrans-go" 5 | ) 6 | 7 | // ChargeReqWithMap : Represent Charge request with map payload 8 | type ChargeReqWithMap map[string]interface{} 9 | 10 | // ChargeReq : Represent Charge request payload 11 | type ChargeReq struct { 12 | PaymentType CoreapiPaymentType `json:"payment_type"` 13 | TransactionDetails midtrans.TransactionDetails `json:"transaction_details"` 14 | 15 | Items *[]midtrans.ItemDetails `json:"item_details,omitempty"` 16 | CustomerDetails *midtrans.CustomerDetails `json:"customer_details,omitempty"` 17 | 18 | CreditCard *CreditCardDetails `json:"credit_card,omitempty"` 19 | BankTransfer *BankTransferDetails `json:"bank_transfer,omitempty"` 20 | EChannel *EChannelDetail `json:"echannel,omitempty"` 21 | Gopay *GopayDetails `json:"gopay,omitempty"` 22 | ShopeePay *ShopeePayDetails `json:"shopeepay,omitempty"` 23 | Qris *QrisDetails `json:"qris,omitempty"` 24 | BCAKlikPay *BCAKlikPayDetails `json:"bca_klikpay,omitempty"` 25 | BCAKlikBCA *BcaKlikBCADetails `json:"bca_klikbca,omitempty"` 26 | MandiriClickPay *MandiriClickPayDetails `json:"mandiri_clickpay,omitempty"` 27 | CIMBClicks *CIMBClicksDetails `json:"cimb_clicks,omitempty"` 28 | 29 | ConvStore *ConvStoreDetails `json:"cstore,omitempty"` 30 | 31 | CustomExpiry *CustomExpiry `json:"custom_expiry,omitempty"` 32 | CustomField1 *string `json:"custom_field1,omitempty"` 33 | CustomField2 *string `json:"custom_field2,omitempty"` 34 | CustomField3 *string `json:"custom_field3,omitempty"` 35 | Metadata interface{} `json:"metadata,omitempty"` 36 | } 37 | 38 | // CreditCardDetails : Represent credit card detail for PaymentTypeCreditCard payment type 39 | type CreditCardDetails struct { 40 | // TokenID represents customer credit card information 41 | TokenID string `json:"token_id"` 42 | 43 | // Authentication Flag to enable the 3D secure authentication. Default value is false. 44 | Authentication bool `json:"authentication,omitempty"` 45 | 46 | // Bank Acquiring bank. Valid values: `midtrans.BankBca` `midtrans.BankMandiri`, `midtrans.BankBni`, 47 | //`midtrans.BankCimb`, `midtrans.BankMaybank`, and `midtrans.BankBri` 48 | Bank string `json:"bank,omitempty"` 49 | 50 | // InstallmentTerm for installment tenor 51 | InstallmentTerm int8 `json:"installment_term,omitempty"` 52 | 53 | // Type Used on preauthorization feature. Valid value: authorize 54 | Type string `json:"type,omitempty"` 55 | 56 | // Bins List of credit card's BIN (Bank Identification Number) that is allowed for transaction 57 | Bins []string `json:"bins,omitempty"` 58 | 59 | // SaveTokenID Used on 'one click' or 'two clicks' feature. Enabling it will return a `Response.SavedCardTokenID` on the response 60 | // and notification body that can be used for the next transaction 61 | SaveTokenID bool `json:"save_token_id,omitempty"` 62 | 63 | // PointRedeemAmount For Mandiri Point, you can only do Full Redemption.(use -1 for Full Redemption) 64 | PointRedeemAmount int64 `json:"point_redeem_amount,omitempty"` 65 | 66 | // Determines how the transaction status is updated to the merchant frontend. Possible values are js_event (default) and form 67 | CallbackType string `json:"callback_type,omitempty"` 68 | } 69 | 70 | // BankTransferDetails : Represent bank_transfer detail 71 | type BankTransferDetails struct { 72 | Bank midtrans.Bank `json:"bank"` 73 | VaNumber string `json:"va_number,omitempty"` 74 | Permata *PermataBankTransferDetail `json:"permata,omitempty"` 75 | FreeText *BCABankTransferDetailFreeText `json:"free_text,omitempty"` 76 | Bca *BcaBankTransferDetail `json:"bca,omitempty"` 77 | } 78 | 79 | // PermataBankTransferDetail : Represent Recipient for bank transfer Permata 80 | type PermataBankTransferDetail struct { 81 | RecipientName string `json:"recipient_name,omitempty"` 82 | } 83 | 84 | // BCABankTransferDetailFreeText : Represent BCA bank_transfer detail free_text 85 | type BCABankTransferDetailFreeText struct { 86 | Inquiry []BCABankTransferLangDetail `json:"inquiry,omitempty"` 87 | Payment []BCABankTransferLangDetail `json:"payment,omitempty"` 88 | } 89 | 90 | // BCABankTransferLangDetail : Represent BCA bank_transfer lang detail 91 | type BCABankTransferLangDetail struct { 92 | LangID string `json:"id,omitempty"` 93 | LangEN string `json:"en,omitempty"` 94 | } 95 | 96 | // BcaBankTransferDetail : BCA sub company code directed for this transactions 97 | // NOTE: Please contact Midtrans Sales Team. 98 | type BcaBankTransferDetail struct { 99 | SubCompanyCode string `json:"sub_company_code,omitempty"` 100 | } 101 | 102 | // EChannelDetail : Represent Mandiri Bill bank transfer detail 103 | type EChannelDetail struct { 104 | BillInfo1 string `json:"bill_info1"` 105 | BillInfo2 string `json:"bill_info2"` 106 | BillInfo3 string `json:"bill_info3,omitempty"` 107 | BillInfo4 string `json:"bill_info4,omitempty"` 108 | BillInfo5 string `json:"bill_info5,omitempty"` 109 | BillInfo6 string `json:"bill_info6,omitempty"` 110 | BillInfo7 string `json:"bill_info7,omitempty"` 111 | BillInfo8 string `json:"bill_info8,omitempty"` 112 | BillKey string `json:"bill_key,omitempty"` 113 | } 114 | 115 | // BCAKlikPayDetails : Represent Internet Banking for BCA KlikPay 116 | type BCAKlikPayDetails struct { 117 | Desc string `json:"description"` 118 | MiscFee int64 `json:"misc_fee,omitempty"` 119 | } 120 | 121 | // BcaKlikBCADetails : Represent Internet Banking BCA KlikBCA detail 122 | type BcaKlikBCADetails struct { 123 | Desc string `json:"description"` 124 | UserID string `json:"user_id"` 125 | } 126 | 127 | // MandiriClickPayDetails : Represent Mandiri ClickPay detail 128 | type MandiriClickPayDetails struct { 129 | // TokenID token id from Get card token Step 130 | TokenID string `json:"token_id"` 131 | Input1 string `json:"input1"` 132 | Input2 string `json:"input2"` 133 | 134 | // Input3 5-digits random number you gave to the customer 135 | Input3 string `json:"input3"` 136 | 137 | // Token Number generated by customer's physical token 138 | Token string `json:"token"` 139 | } 140 | 141 | // CIMBClicksDetails : Represent CIMB Clicks detail 142 | type CIMBClicksDetails struct { 143 | Desc string `json:"description"` 144 | } 145 | 146 | // QrisDetails QRIS is a QR payment standard in Indonesia that is developed by Bank Indonesia (BI). 147 | // Users could scan and pay the QR from any payment providers registered as the issuer 148 | type QrisDetails struct { 149 | Acquirer string `json:"acquirer,omitempty"` 150 | } 151 | 152 | // ConvStoreDetails : Represent cstore detail 153 | type ConvStoreDetails struct { 154 | Store string `json:"store"` 155 | Message string `json:"message,omitempty"` 156 | 157 | AlfamartFreeText1 string `json:"alfamart_free_text_1,omitempty"` 158 | AlfamartFreeText2 string `json:"alfamart_free_text_2,omitempty"` 159 | AlfamartFreeText3 string `json:"alfamart_free_text_3,omitempty"` 160 | } 161 | 162 | // GopayDetails : Represent gopay detail 163 | type GopayDetails struct { 164 | EnableCallback bool `json:"enable_callback,omitempty"` // To determine appending callback url in the deeplink. Default value: false 165 | CallbackUrl string `json:"callback_url,omitempty"` // To determine where GO-JEK apps will redirect after successful payment. Can be HTTP or deeplink url. Default value: callback_url in dashboard settings 166 | AccountID string `json:"account_id,omitempty"` // Required for GoPay tokenization. Linked customer account ID from create pay account API. 167 | PaymentOptionToken string `json:"payment_option_token,omitempty"` // Required for GoPay tokenization. Token to specify the payment option made by the customer from get pay account API metadata. 168 | PreAuth bool `json:"pre_auth,omitempty"` // To make payment mode into reservation of customer balance only. Once, customer balance is reserved, a subsequent capture call is expected to be initiated by merchants. 169 | Recurring bool `json:"recurring,omitempty"` 170 | } 171 | 172 | // ShopeePayDetails : Represent shopeepay detail 173 | type ShopeePayDetails struct { 174 | CallbackUrl string `json:"callback_url,omitempty"` 175 | } 176 | 177 | // CustomExpiry : Represent Core API custom_expiry 178 | type CustomExpiry struct { 179 | // OrderTime Time when the order is created in merchant website. Format: yyyy-MM-dd hh:mm:ss Z. 180 | // If attribute undefined, expiry time starts from transaction time 181 | OrderTime string `json:"order_time,omitempty"` 182 | 183 | // ExpiryDuration Time duration the payment will remain valid 184 | ExpiryDuration int `json:"expiry_duration,omitempty"` 185 | 186 | // Unit for expiry_duration. Valid values are: second, minute, hour, or day. 187 | // NOTE: If attribute undefined, default unit is minute 188 | Unit string `json:"unit,omitempty"` 189 | } 190 | 191 | // CaptureReq : Represent Capture request payload 192 | type CaptureReq struct { 193 | TransactionID string `json:"transaction_id"` 194 | GrossAmt float64 `json:"gross_amount"` 195 | } 196 | 197 | // RefundReq : Represent Refund request payload 198 | type RefundReq struct { 199 | RefundKey string `json:"refund_key"` 200 | Amount int64 `json:"amount"` 201 | Reason string `json:"reason"` 202 | } 203 | 204 | type SubscriptionReq struct { 205 | // Name Subscription's name that will be used to generate transaction's order id. 206 | // Note: Allowed symbols are dash(-), underscore(_), tilde (~), and dot (.) 207 | Name string `json:"name"` 208 | 209 | // Amount that will be used to make recurring charge. Note: Do not use decimal 210 | Amount int64 `json:"amount"` 211 | 212 | // Currency ISO-4217 representation for 3 digit alphabetic currency code. Note: Currently only support IDR 213 | Currency string `json:"currency"` 214 | 215 | // PaymentType Transaction payment method. Note: currently only support credit_card and gopay 216 | PaymentType SubscriptionPaymentType `json:"payment_type"` 217 | 218 | // Token Saved payment token. Note: For `credit_card` should use `saved_token_id` received in charge response. 219 | // For gopay should use payment_options. token received in get pay account response 220 | Token string `json:"token"` 221 | 222 | // Schedule Subscription schedule details 223 | Schedule ScheduleDetails `json:"schedule"` 224 | 225 | // Metadata of subscription from merchant, the size must be less than 1KB 226 | Metadata interface{} `json:"metadata,omitempty"` 227 | 228 | // CustomerDetails Customer details information 229 | CustomerDetails *midtrans.CustomerDetails `json:"customer_details,omitempty"` 230 | 231 | // Gopay subscription information, required if payment type is gopay 232 | Gopay *GopaySubscriptionDetails `json:"gopay,omitempty"` 233 | } 234 | 235 | type GopaySubscriptionDetails struct { 236 | AccountId string `json:"account_id"` // Gopay Account ID from Core API 237 | } 238 | 239 | //ScheduleDetails Create Subscription schedule object 240 | type ScheduleDetails struct { 241 | // Subscription's interval given by merchant 242 | Interval int `json:"interval"` 243 | 244 | // Interval temporal unit Note: currently only support day, week, and month 245 | IntervalUnit string `json:"interval_unit"` 246 | 247 | // MaxInterval Maximum interval of subscription. Subscription will end after maximum interval is reached 248 | MaxInterval int `json:"max_interval"` 249 | 250 | // StartTime Timestamp of subscription, format: yyyy-MM-dd HH:mm:ss Z. The value must be after the current time. 251 | // If specified, first payment will happen on start_time. If start_time is not specified, the default value for 252 | // start_time will be current time and first payment will happen on one interval after current time. 253 | StartTime string `json:"start_time,omitempty"` 254 | } 255 | 256 | type PaymentAccountReq struct { 257 | PaymentType CoreapiPaymentType `json:"payment_type"` // Payment channel where the account register to 258 | GopayPartner *GopayPartnerDetails `json:"gopay_partner"` // GoPay linking specific parameters 259 | } 260 | 261 | type GopayPartnerDetails struct { 262 | PhoneNumber string `json:"phone_number"` // Phone number linked to the customer account 263 | CountryCode string `json:"country_code"` // Country code associated to the phone number 264 | RedirectURL string `json:"redirect_url,omitempty"` // URL where user will be redirected to after finishing the confirmation on Gojek app 265 | } 266 | -------------------------------------------------------------------------------- /coreapi/response.go: -------------------------------------------------------------------------------- 1 | package coreapi 2 | 3 | import "github.com/midtrans/midtrans-go" 4 | 5 | type ResponseWithMap map[string]interface{} 6 | 7 | // VANumber : bank virtual account number 8 | type VANumber struct { 9 | Bank string `json:"bank"` 10 | VANumber string `json:"va_number"` 11 | } 12 | 13 | // Action represents response action 14 | type Action struct { 15 | Name string `json:"name"` 16 | Method string `json:"method"` 17 | URL string `json:"url"` 18 | Fields []string `json:"fields"` 19 | } 20 | 21 | type PaymentAmount struct { 22 | PaidAt string `json:"paid_at"` 23 | Amount string `json:"amount"` 24 | } 25 | 26 | // ChargeResponse : CoreAPI charge response struct when calling Midtrans API 27 | type ChargeResponse struct { 28 | TransactionID string `json:"transaction_id"` 29 | OrderID string `json:"order_id"` 30 | GrossAmount string `json:"gross_amount"` 31 | PaymentType string `json:"payment_type"` 32 | TransactionTime string `json:"transaction_time"` 33 | TransactionStatus string `json:"transaction_status"` 34 | FraudStatus string `json:"fraud_status"` 35 | MaskedCard string `json:"masked_card"` 36 | StatusCode string `json:"status_code"` 37 | Bank string `json:"bank"` 38 | StatusMessage string `json:"status_message"` 39 | ApprovalCode string `json:"approval_code"` 40 | ChannelResponseCode string `json:"channel_response_code"` 41 | ChannelResponseMessage string `json:"channel_response_message"` 42 | Currency string `json:"currency"` 43 | CardType string `json:"card_type"` 44 | RedirectURL string `json:"redirect_url"` 45 | ID string `json:"id"` 46 | ValidationMessages []string `json:"validation_messages"` 47 | InstallmentTerm string `json:"installment_term"` 48 | Eci string `json:"eci"` 49 | SavedTokenID string `json:"saved_token_id"` 50 | SavedTokenIDExpiredAt string `json:"saved_token_id_expired_at"` 51 | PointRedeemAmount int `json:"point_redeem_amount"` 52 | PointRedeemQuantity int `json:"point_redeem_quantity"` 53 | PointBalanceAmount string `json:"point_balance_amount"` 54 | PermataVaNumber string `json:"permata_va_number"` 55 | VaNumbers []VANumber `json:"va_numbers"` 56 | BillKey string `json:"bill_key"` 57 | BillerCode string `json:"biller_code"` 58 | Acquirer string `json:"acquirer"` 59 | Actions []Action `json:"actions"` 60 | PaymentCode string `json:"payment_code"` 61 | Store string `json:"store"` 62 | QRString string `json:"qr_string"` 63 | OnUs bool `json:"on_us"` 64 | ThreeDsVersion string `json:"three_ds_version"` 65 | ExpiryTime string `json:"expiry_time"` 66 | } 67 | 68 | // ApproveResponse : Approve response type when calling Midtrans approve transaction API 69 | type ApproveResponse = ChargeResponse 70 | 71 | // DenyResponse : Deny response type when calling Midtrans deny transaction API 72 | type DenyResponse = ChargeResponse 73 | 74 | // CancelResponse : Cancel response type when calling Midtrans cancel transaction API 75 | type CancelResponse = ChargeResponse 76 | 77 | // ExpireResponse : Expire response type when calling Midtrans expire transaction API 78 | type ExpireResponse = ChargeResponse 79 | 80 | // CaptureResponse : Capture response type when calling Midtrans API capture for credit card transaction 81 | type CaptureResponse = ChargeResponse 82 | 83 | // TransactionStatusResponse : Status transaction response struct 84 | type TransactionStatusResponse struct { 85 | TransactionTime string `json:"transaction_time"` 86 | GrossAmount string `json:"gross_amount"` 87 | Currency string `json:"currency"` 88 | OrderID string `json:"order_id"` 89 | PaymentType string `json:"payment_type"` 90 | SignatureKey string `json:"signature_key"` 91 | StatusCode string `json:"status_code"` 92 | TransactionID string `json:"transaction_id"` 93 | TransactionStatus string `json:"transaction_status"` 94 | FraudStatus string `json:"fraud_status"` 95 | SettlementTime string `json:"settlement_time"` 96 | StatusMessage string `json:"status_message"` 97 | MerchantID string `json:"merchant_id"` 98 | PermataVaNumber string `json:"permata_va_number"` 99 | VaNumbers []VANumber `json:"va_numbers"` 100 | PaymentAmounts []PaymentAmount `json:"payment_amounts"` 101 | ID string `json:"id"` 102 | PaymentCode string `json:"payment_code"` 103 | Store string `json:"store"` 104 | MaskedCard string `json:"masked_card"` 105 | Bank string `json:"bank"` 106 | ApprovalCode string `json:"approval_code"` 107 | Eci string `json:"eci"` 108 | ChannelResponseCode string `json:"channel_response_code"` 109 | ChannelResponseMessage string `json:"channel_response_message"` 110 | CardType string `json:"card_type"` 111 | Refunds []RefundDetails `json:"refunds"` 112 | RefundAmount string `json:"refund_amount"` 113 | BillKey string `json:"bill_key"` 114 | BillerCode string `json:"biller_code"` 115 | TransactionType string `json:"transaction_type"` 116 | Issuer string `json:"issuer"` 117 | Acquirer string `json:"acquirer"` 118 | CustomField1 string `json:"custom_field1"` 119 | CustomField2 string `json:"custom_field2"` 120 | CustomField3 string `json:"custom_field3"` 121 | Metadata interface{} `json:"metadata"` 122 | PaymentOptionsType string `json:"payment_options_type"` 123 | InstallmentTerm int `json:"installment_term"` 124 | ThreeDsVersion string `json:"three_ds_version"` 125 | ExpiryTime string `json:"expiry_time"` 126 | } 127 | 128 | type TransactionStatusB2bResponse struct { 129 | StatusCode string `json:"status_code"` 130 | StatusMessage string `json:"status_message"` 131 | ID string `json:"id"` 132 | Transactions []TransactionStatusResponse `json:"transactions"` 133 | } 134 | 135 | // RefundDetails Details 136 | type RefundDetails struct { 137 | RefundChargebackID int `json:"refund_chargeback_id"` 138 | RefundChargebackUUID string `json:"refund_chargeback_uuid"` 139 | RefundAmount string `json:"refund_amount"` 140 | Reason string `json:"reason"` 141 | RefundKey string `json:"refund_key"` 142 | RefundMethod string `json:"refund_method"` 143 | BankConfirmedAt string `json:"bank_confirmed_at"` 144 | CreatedAt string `json:"created_at"` 145 | } 146 | 147 | // RefundResponse : Refund response struct when calling Midtrans refund and direct refund API 148 | type RefundResponse struct { 149 | StatusCode string `json:"status_code"` 150 | StatusMessage string `json:"status_message"` 151 | ID string `json:"id"` 152 | TransactionID string `json:"transaction_id"` 153 | OrderID string `json:"order_id"` 154 | GrossAmount string `json:"gross_amount"` 155 | Currency string `json:"currency"` 156 | MerchantID string `json:"merchant_id"` 157 | PaymentType string `json:"payment_type"` 158 | TransactionTime string `json:"transaction_time"` 159 | TransactionStatus string `json:"transaction_status"` 160 | SettlementTime string `json:"settlement_time"` 161 | FraudStatus string `json:"fraud_status"` 162 | RefundChargebackID int `json:"refund_chargeback_id"` 163 | RefundChargebackUUID string `json:"refund_chargeback_uuid"` 164 | RefundAmount string `json:"refund_amount"` 165 | RefundKey string `json:"refund_key"` 166 | } 167 | 168 | type CardTokenResponse struct { 169 | StatusCode string `json:"status_code"` 170 | StatusMessage string `json:"status_message"` 171 | ValidationMessage []string `json:"validation_messages"` 172 | Id string `json:"id"` 173 | TokenID string `json:"token_id"` 174 | Hash string `json:"hash"` 175 | RedirectURL string `json:"redirect_url"` 176 | Bank string `json:"bank"` 177 | } 178 | 179 | type CardRegisterResponse struct { 180 | StatusCode string `json:"status_code"` 181 | StatusMessage string `json:"status_message"` 182 | ValidationMessage []string `json:"validation_messages"` 183 | Id string `json:"id"` 184 | SavedTokenID string `json:"saved_token_id"` 185 | TransactionID string `json:"transaction_id"` 186 | MaskCard string `json:"masked_card"` 187 | } 188 | 189 | type BinResponse struct { 190 | Data struct { 191 | RegistrationRequired bool `json:"registration_required"` 192 | CountryName string `json:"country_name"` 193 | CountryCode string `json:"country_code"` 194 | Channel string `json:"channel"` 195 | Brand string `json:"brand"` 196 | BinType string `json:"bin_type"` 197 | BinClass string `json:"bin_class"` 198 | Bin string `json:"bin"` 199 | BankCode string `json:"bank_code"` 200 | Bank string `json:"bank"` 201 | } `json:"data"` 202 | } 203 | 204 | type CreateSubscriptionResponse struct { 205 | ID string `json:"id"` 206 | Name string `json:"name"` 207 | Amount string `json:"amount"` 208 | Currency string `json:"currency"` 209 | CreatedAt string `json:"created_at"` 210 | Schedule ScheduleResponse `json:"schedule"` 211 | Status string `json:"status"` 212 | Token string `json:"token"` 213 | PaymentType string `json:"payment_type"` 214 | Metadata interface{} `json:"metadata"` 215 | CustomerDetails midtrans.CustomerDetails `json:"customer_details"` 216 | TransactionId []string `json:"transaction_id"` 217 | 218 | StatusMessage string `json:"status_message"` 219 | ValidationMessage []string `json:"validation_message"` 220 | } 221 | 222 | type StatusSubscriptionResponse = CreateSubscriptionResponse 223 | 224 | type UpdateSubscriptionResponse struct { 225 | StatusMessage string `json:"status_message"` 226 | } 227 | 228 | type EnableSubscriptionResponse = UpdateSubscriptionResponse 229 | type DisableSubscriptionResponse = UpdateSubscriptionResponse 230 | 231 | // ScheduleResponse Subscription schedule response object 232 | type ScheduleResponse struct { 233 | Interval int `json:"interval"` 234 | IntervalUnit string `json:"interval_unit"` 235 | MaxInterval int `json:"max_interval"` 236 | CurrentInterval int `json:"current_interval"` 237 | StartTime string `json:"start_time"` 238 | PreviousExecutionAt string `json:"previous_execution_at"` 239 | NextExecutionAt string `json:"next_execution_at"` 240 | } 241 | 242 | type PaymentAccountResponse struct { 243 | StatusCode string `json:"status_code"` 244 | PaymentType string `json:"payment_type"` 245 | AccountId string `json:"account_id"` 246 | AccountStatus string `json:"account_status"` 247 | ChannelResponseCode string `json:"channel_response_code"` 248 | ChannelResponseMessage string `json:"channel_response_message"` 249 | Actions []Action `json:"actions"` 250 | Metadata PaymentAccountMetadataDetails `json:"metadata"` 251 | StatusMessage string `json:"status_message"` 252 | ID string `json:"id"` 253 | } 254 | 255 | type PaymentAccountMetadataDetails struct { 256 | PaymentOptions []PaymentOptionsDetails `json:"payment_options"` 257 | } 258 | 259 | type PaymentOptionsDetails struct { 260 | Name string `json:"name"` 261 | Active bool `json:"active"` 262 | Metadata interface{} `json:"metadata"` 263 | Balance BalanceDetails `json:"balance"` 264 | Token string `json:"token"` 265 | } 266 | 267 | type BalanceDetails struct { 268 | Value string `json:"value"` 269 | Currency string `json:"currency"` 270 | } 271 | 272 | type PointInquiryResponse struct { 273 | StatusCode string `json:"status_code"` 274 | StatusMessage string `json:"status_message"` 275 | PointBalance int `json:"point_balance"` 276 | TransactionTime string `json:"transaction_time"` 277 | PointBalanceAmount string `json:"point_balance_amount"` 278 | } 279 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package midtrans 2 | 3 | import "fmt" 4 | 5 | type Error struct { 6 | Message string 7 | StatusCode int 8 | RawError error 9 | RawApiResponse *ApiResponse 10 | } 11 | 12 | // Error returns error message. 13 | // To comply midtrans.Error with Go error interface. 14 | func (e *Error) Error() string { 15 | if e.RawError != nil { 16 | return fmt.Sprintf("%s: %s", e.Message, e.RawError.Error()) 17 | } 18 | return e.Message 19 | } 20 | 21 | // Unwrap method that returns its contained error 22 | // if there is RawError supplied during error creation, return RawError. Else, will return nil 23 | func (e *Error) Unwrap() error { 24 | return e.RawError 25 | } 26 | 27 | // GetMessage this get general message error when call api 28 | func (e *Error) GetMessage() string { 29 | return e.Message 30 | } 31 | 32 | // GetStatusCode this get api response status code coming from midtrans backend 33 | func (e *Error) GetStatusCode() int { 34 | return e.StatusCode 35 | } 36 | 37 | // GetRawApiResponse this get api raw response from midtrans backend 38 | func (e *Error) GetRawApiResponse() *ApiResponse { 39 | return e.RawApiResponse 40 | } 41 | 42 | // GetRawError GetRawApiResponse this get api raw response from midtrans backend 43 | func (e *Error) GetRawError() error { 44 | return e.RawError 45 | } 46 | -------------------------------------------------------------------------------- /error_test.go: -------------------------------------------------------------------------------- 1 | package midtrans 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "net/http" 8 | "testing" 9 | 10 | assert "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestErrorStruct(t *testing.T) { 14 | err := &Error{ 15 | Message: "Error Test Message", 16 | StatusCode: 200, 17 | RawError: errors.New("TEST FROM GO ERROR"), 18 | RawApiResponse: nil, 19 | } 20 | var midError *Error 21 | assert.Error(t, err) 22 | assert.True(t, true, errors.Is(err, err)) 23 | assert.True(t, true, errors.As(err, &midError)) 24 | assert.Equal(t, `Error Test Message`, err.GetMessage()) 25 | assert.Equal(t, 200, err.GetStatusCode()) 26 | assert.Equal(t, "Error Test Message: TEST FROM GO ERROR", err.Error()) 27 | } 28 | 29 | func TestErrorResponse(t *testing.T) { 30 | serverKey := "dummy" 31 | c := GetHttpClient(Environment) 32 | jsonReq, _ := json.Marshal("{\"transaction_details\": {\"order_id\": \"TEST-1648108994111\", \"gross_amount\": 10000}}") 33 | err := c.Call(http.MethodPost, "https://app.midtrans.com/snap/v1/transactions", &serverKey, nil, bytes.NewBuffer(jsonReq), nil) 34 | 35 | var midError *Error 36 | assert.True(t, true, errors.Is(err, err)) 37 | assert.True(t, true, errors.As(err, &midError)) 38 | assert.Error(t, err) 39 | assert.Equal(t, 401, err.StatusCode) 40 | assert.Equal(t, "app.midtrans.com", err.RawApiResponse.Request.Host) 41 | } -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Sample App 2 | This is a very simple, very minimalist example to demonstrate integrating Midtrans with Go 3 | 4 | ## Run The app 5 | 1. Clone the repository, open terminal in this `/example/simple/coreapi-card-3ds` folder. 6 | 2. Run the web server using: `go run main.go` 7 | 3. The smple app will run at port 3000. Open `localhost:3000` from browser. 8 | 9 | ## Run command line app 10 | 1. Clone the repository, open terminal in this `/example/simple/coreapi` folder. 11 | 2. Run the app using: `go run main.go` 12 | -------------------------------------------------------------------------------- /example/mockup.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/midtrans/midtrans-go" 5 | "github.com/midtrans/midtrans-go/coreapi" 6 | "github.com/midtrans/midtrans-go/snap" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | const SandboxServerKey1 = "SB-Mid-server-TvgWB_Y9s81-rbMBH7zZ8BHW" 12 | const SandboxServerKey2 = "SB-Mid-server-TOq1a2AVuiyhhOjvfs3U_KeO" 13 | 14 | const SandboxClientKey2 = "SB-Mid-client-nKsqvar5cn60u2Lv" 15 | 16 | 17 | const IrisCreatorKeySandbox = "IRIS-330198f0-e49d-493f-baae-585cfded355d" 18 | const IrisApproverKeySandbox = "IRIS-1595c12b-6814-4e5a-bbbb-9bc18193f47b" 19 | 20 | 21 | func SnapParamWithMap() *snap.RequestParamWithMap { 22 | req := &snap.RequestParamWithMap{ 23 | "transaction_details": map[string]interface{}{ 24 | "order_id": "MID-GO-TEST-" + Random(), 25 | "gross_amount": 10000, 26 | }, 27 | } 28 | return req 29 | 30 | } 31 | 32 | func SnapParam() *snap.Request { 33 | req := & snap.Request{ 34 | TransactionDetails: midtrans.TransactionDetails{ 35 | OrderID: "MID-GO-TEST-" + Random(), 36 | GrossAmt: 100000, 37 | }, 38 | } 39 | return req 40 | } 41 | 42 | func CoreParam() *coreapi.ChargeReqWithMap { 43 | req := &coreapi.ChargeReqWithMap{ 44 | "payment_type": "gopay", 45 | "transaction_details": map[string]interface{}{ 46 | "order_id": "MID-GO-TEST-" + Random(), 47 | "gross_amount": 10000, 48 | }, 49 | } 50 | return req 51 | } 52 | 53 | func Random() string { 54 | time.Sleep(500 * time.Millisecond) 55 | return strconv.FormatInt(time.Now().Unix(), 10) 56 | } -------------------------------------------------------------------------------- /example/simple/coreapi-card-3ds/main.go: -------------------------------------------------------------------------------- 1 | // This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely. 2 | 3 | package main 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "github.com/midtrans/midtrans-go" 9 | "github.com/midtrans/midtrans-go/coreapi" 10 | "html/template" 11 | "log" 12 | "net/http" 13 | "path" 14 | "strconv" 15 | "time" 16 | ) 17 | 18 | // Set Your server key 19 | // You can find it in Merchant Portal -> Settings -> Access keys 20 | const SERVER_KEY string = "SB-Mid-server-1isH_dlGSg6uy.I7NpeNK53i" 21 | const CLIENT_KEY string = "SB-Mid-client-yrY4WjUNOnhOyIIH" 22 | 23 | type CardTokenAndAuthRequest struct { 24 | TokenID string `json:"token_id"` 25 | Secure bool `json:"authenticate_3ds"` 26 | } 27 | 28 | type StatusTransactionRequest struct { 29 | TransactionID string `json:"transaction_id"` 30 | } 31 | 32 | func main() { 33 | mux := http.NewServeMux() 34 | mux.HandleFunc("/", HomeHandler) 35 | mux.HandleFunc("/charge_core_api_ajax", ChargeAjaxHandler) 36 | mux.HandleFunc("/check_transaction_status", StatusAjaxHandler) 37 | 38 | log.Println("Starting web on port 3000") 39 | err := http.ListenAndServe(":3000", mux) 40 | log.Fatal(err) 41 | } 42 | 43 | func HomeHandler(w http.ResponseWriter, r *http.Request) { 44 | 45 | fmt.Println(generateOrderIdSuffix) 46 | if r.URL.Path != "/" { 47 | http.NotFound(w, r) 48 | return 49 | } 50 | 51 | needCredential := false 52 | if len(SERVER_KEY) == 0 || len(CLIENT_KEY) == 0 { 53 | needCredential = true 54 | } 55 | 56 | templ, err := template.ParseFiles(path.Join("views", "index.html")) 57 | if err != nil { 58 | log.Println(err) 59 | http.Error(w, "template file is not found", http.StatusInternalServerError) 60 | return 61 | } 62 | 63 | data := map[string]interface{} { 64 | "clientKey": CLIENT_KEY, 65 | "needCredential": needCredential, 66 | } 67 | err = templ.Execute(w, data) 68 | if err != nil { 69 | log.Println(err) 70 | http.Error(w, "template file is not found", http.StatusInternalServerError) 71 | return 72 | } 73 | } 74 | 75 | func ChargeAjaxHandler(w http.ResponseWriter, r *http.Request) { 76 | var requestBody CardTokenAndAuthRequest 77 | err := json.NewDecoder(r.Body).Decode(&requestBody) 78 | if err != nil { 79 | http.Error(w, err.Error(), http.StatusBadRequest) 80 | return 81 | } 82 | 83 | var c = coreapi.Client{} 84 | c.New(SERVER_KEY, midtrans.Sandbox) 85 | 86 | chargeReq := &coreapi.ChargeReq{ 87 | PaymentType: coreapi.PaymentTypeCreditCard, 88 | TransactionDetails: midtrans.TransactionDetails{ 89 | OrderID: "MID-GO-TEST-" + generateOrderIdSuffix(), 90 | GrossAmt: 200000, 91 | }, 92 | CreditCard: &coreapi.CreditCardDetails{ 93 | TokenID: requestBody.TokenID, 94 | Authentication: requestBody.Secure, 95 | }, 96 | Items: &[]midtrans.ItemDetails{ 97 | midtrans.ItemDetails{ 98 | ID: "ITEM1", 99 | Price: 200000, 100 | Qty: 1, 101 | Name: "Someitem", 102 | }, 103 | }, 104 | } 105 | 106 | res, _ := c.ChargeTransaction(chargeReq) 107 | response, _ := json.Marshal(res) 108 | 109 | w.Header().Set("Content-Type", "application/json") 110 | w.Write(response) 111 | } 112 | 113 | func StatusAjaxHandler(w http.ResponseWriter, r *http.Request) { 114 | var requestBody StatusTransactionRequest 115 | err := json.NewDecoder(r.Body).Decode(&requestBody) 116 | if err != nil { 117 | http.Error(w, err.Error(), http.StatusBadRequest) 118 | return 119 | } 120 | 121 | var c = coreapi.Client{} 122 | c.New(SERVER_KEY, midtrans.Sandbox) 123 | res, _ := c.CheckTransaction(requestBody.TransactionID) 124 | response, _ := json.Marshal(res) 125 | 126 | w.Header().Set("Content-Type", "application/json") 127 | w.Write(response) 128 | } 129 | 130 | func generateOrderIdSuffix() string { 131 | return strconv.FormatInt(time.Now().Unix(), 10) 132 | } 133 | -------------------------------------------------------------------------------- /example/simple/coreapi-card-3ds/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |JSON result will appear here after payment:
Please set your server key and client key from sandbox In file: main.go
14 | // Set Your server key
15 | // You can find it in Merchant Portal -> Settings -> Access keys
16 |
SERVER_KEY = ""
17 |
CLIENT_KEY = ""
18 |
19 | {{end}}
20 |
52 | Transaction Result:
53 | Awaiting transactions...
54 | Transaction verified status result:
55 | Awaiting transactions...
56 |
57 | Testing cards:
58 |
59 | For 3D Secure:
60 | Visa success 4811 1111 1111 1114
61 | Visa deny by bank 4711 1111 1111 1115
62 | Visa deny by FDS 4611 1111 1111 1116
63 |
64 | MasterCard success 5211 1111 1111 1117
65 | MasterCard deny by bank 5111 1111 1111 1118
66 | MasterCard deny by FDS 5411 1111 1111 1115
67 |
68 | Challenge by FDS 4511 1111 1111 1117
69 |
70 |
71 |
72 |