diff --git a/README.md b/README.md index 88613c9..0434185 100644 --- a/README.md +++ b/README.md @@ -35,15 +35,6 @@ Payment module used as proxy for multiple payment gateways. Currently it only su - [Mandatory Environment Variables](#mandatory-environment-variables) - [Example Code](#example-code) - [API Usage](#api-usage) - - [POSTMAN JSON](#postman-json) - - [List of payment methods](#list-of-payment-methods) - - [Request](#request) - - [Response](#response) - - [Generating New Invoice](#generating-new-invoice) - - [Request](#request-1) - - [Response](#response-1) - - [For Midtrans Payment Channel](#for-midtrans-payment-channel) - - [For Xendit Payment Channel](#for-xendit-payment-channel) - [Contributing](#contributing) - [License](#license) @@ -55,13 +46,14 @@ Payment module used as proxy for multiple payment gateways. Currently it only su In general, this payment proxy can support payment through this following channels: +- Recurring payment with Credit Card, OVO, Bank Transfer - Credit card payment with/without installment - Ewallet (GoPay, OVO, Dana, LinkAja) - Retail Outlet (Alfamart, Alfamidi, Dan+Dan) - Cardless Credit (Akulaku) -- Bank Transfer via Virtual Account (BCA, BNI, Mandiri, Permata, Other Bank). BRI channel is coming. +- Bank Transfer via Virtual Account (BCA, BNI, BRI, Mandiri, Permata, Other Bank). -> :heavy_exclamation_mark: Support for recurring payment will be added soon! +> :heavy_exclamation_mark: Recurring payment is only supported via XenditInvoice. ## Why you should use this payment proxy? @@ -98,7 +90,7 @@ This tables shows which payment channels that has been implemented by this proxy | Other VA | :white_check_mark: | :x: | | BRI VA | :x: | :white_check_mark: | | Alfamart, Alfamidi, Dan+Dan | :white_check_mark: | :white_check_mark: | -| QRIS | :white_check_mark: via Gopay Option | :x: | +| QRIS | :white_check_mark: | n/a | | Gopay | :white_check_mark: | :x: | | OVO | :x: | :white_check_mark: | | DANA | :x: | :white_check_mark: | @@ -303,195 +295,11 @@ export FAILED_REDIRECT_PATH="/donate/error" ## Example Code -To start using this module, you can try the example [server.go](/example/server/server.go) - -```go -package main - -import ( - "net/http" - - "github.com/gorilla/mux" - "github.com/imrenagi/go-payment/datastore/inmemory" - dsmysql "github.com/imrenagi/go-payment/datastore/mysql" - "github.com/imrenagi/go-payment/gateway/midtrans" - "github.com/imrenagi/go-payment/invoice" - "github.com/imrenagi/go-payment/manage" - "github.com/imrenagi/go-payment/server" - "github.com/imrenagi/go-payment/util/db/mysql" - "github.com/imrenagi/go-payment/util/localconfig" - "github.com/rs/cors" - "github.com/rs/zerolog/log" -) - -func main() { - - secret, err := localconfig.LoadSecret("example/server/secret.yaml") - if err != nil { - panic(err) - } - - db := mysql.NewGorm(secret.DB) - db.AutoMigrate( - &midtrans.TransactionStatus{}, - &invoice.Invoice{}, - &invoice.Payment{}, - &invoice.CreditCardDetail{}, - &invoice.LineItem{}, - &invoice.BillingAddress{}, - ) - - m := manage.NewManager(secret.Payment) - m.MustMidtransTransactionStatusRepository(dsmysql.NewMidtransTransactionRepository(db)) - m.MustInvoiceRepository(dsmysql.NewInvoiceRepository(db)) - m.MustPaymentConfigReader(inmemory.NewPaymentConfigRepository("example/server/payment-methods.yml")) - - srv := srv{ - Router: mux.NewRouter(), - paymentSrv: server.NewServer(m), - } - srv.routes() - - if err := http.ListenAndServe(":8080", srv.GetHandler()); err != nil { - log.Fatal().Msgf("Server can't run. Got: `%v`", err) - } - -} -``` - -To run the application, simply use: - -```console -$ go run example/server/server.go -``` - -> :heavy_exclamation_mark: If you want to accept payment callback from the payment gateway on your local computer for development purpose, consider to use [ngrok.io](https://ngrok.io) to expose your localhost to the internet and update the callback base URL in payment gateway dashboard and `SERVER_BASE_URL` accordingly. +You can find the sample code in [here](/example/server) ## API Usage -Your client webservice can interact to at least 2 endpoints: - -- GET all payment methods available -- POST generating new invoice - -### POSTMAN JSON - -You can download this POSTMAN json file to see how to use the api. [POSTMAN COLLECTION](/example/server/go-payment.postman_collection.json) - -### List of payment methods - -#### Request - -If you want to get the estimated admin/installment fee for each payment methods, provice this GET request with optional `price` and `currency` query. Otherwise, it returns nil `admin_fee` and `installment_fee` - -```http -GET /payment/methods?price=1000¤cy=IDR -``` - -#### Response - -```json -{ - "card_payment": { - "payment_type": "credit_card", - "installments": [ - { - "display_name": "", - "type": "offline", - "bank": "bca", - "terms": [ - { - "term": 0, - "admin_fee": { - "value": 2029, - "curency": "IDR" - } - } - ] - } - ] - }, - // ... redacted ... - "ewallets": [ - { - "payment_type": "gopay", - "display_name": "Gopay", - "admin_fee": { - "value": 0, - "curency": "IDR" - } - } - ] -} -``` - -### Generating New Invoice - -Use this endpoint to create a payment request to chosen payment channels and gateway. - -#### Request - -```http -POST /payment/invoices -``` - -```json -{ - "payment": { - "payment_type": "ovo" - }, - "customer": { - "name": "John", - "email": "foo@example.com", - "phone_number": "089922222222" - }, - "items": [{ - "name": "Support Podcast", - "category": "PODCAST", - "merchant": "imrenagi.com", - "description": "donasi podcast imre nagi", - "qty": 1, - "price": 80001, - "currency": "IDR" - }] -} -``` - -> To create invoice with `credit_card` payment with/without installment, please take a look [POSTMAN COLLECTION](/example/server/go-payment.postman_collection.json) - -#### Response - -When you call endpoint above, server returns all invoice data. But, to proceed to the payment page you need to pay attention to `payment` object. - -```json -{ - "payment": { - "id": 48, - "created_at": "2020-05-25T23:31:44.99873+07:00", - "updated_at": "2020-05-25T23:31:44.99873+07:00", - "deleted_at": null, - "gateway": "xendit", - "payment_type": "ovo", - "token": "", - "redirect_url": "https://invoice.xendit.co/web/invoices5ecbf2f0689543409347ec15", - "transaction_id": "5ecbf2f0689543409347ec15" - } -} -``` - -:heavy_exclamation_mark::heavy_exclamation_mark::heavy_exclamation_mark: Please note: - -#### For Midtrans Payment Channel - -- Value of `payment.gateway` will is always `midtrans` -- You can use `payment.token` to open snap window by using midtrans [snap.js](https://snap-docs.midtrans.com/#snap-js) -- If you want to use [Window Redirection](https://snap-docs.midtrans.com/#window-redirection), you can open a new browser tab by using url in `payment.redirect_url` - -#### For Xendit Payment Channel - -- Value of `payment.gateway` will is always `xendit` -- `payment.token` is always empty for all xendit provided payment channels -- You will always open `payment.redirect_url` in new browser tap for all payment methods provided by xendit. Including DANA, LinkAja, Kredivo, even Xendit Invoice. +You can find the details of API usage in [here](/docs) ## Contributing diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..fcaad6a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,10 @@ +How to Use API +=== + +## Table of Contents: + +- [Postman Collections](./go-payment.postman_collection.json) +- [Payment Methods](./methods.md) +- [Invoices](./invoice.md) +- [Subscription](./subscription.md) + diff --git a/docs/go-payment.postman_collection.json b/docs/go-payment.postman_collection.json new file mode 100644 index 0000000..9880724 --- /dev/null +++ b/docs/go-payment.postman_collection.json @@ -0,0 +1,275 @@ +{ + "info": { + "_postman_id": "8193a2d1-3a85-404d-a789-6e672e80d716", + "name": "go-payment", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Payment", + "item": [ + { + "name": "Non Credit Card Payment", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n\t\"payment\": {\n\t\t\"payment_type\": \"alfamart\"\n\t},\n\t\"customer\": {\n\t\t\"name\": \"John\",\n\t\t\"email\": \"foo@bar.com\",\n\t\t\"phone_number\": \"\"\n\t},\n\t\"items\": [{\n\t\t\"name\": \"Support Podcast\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 10000,\n\t\t\"currency\": \"IDR\"\n\t},{\n\t\t\"name\": \"Support Podcast 2\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 20000,\n\t\t\"currency\": \"IDR\"\n\t}]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/payment/invoices", + "host": [ + "{{base_url}}" + ], + "path": [ + "payment", + "invoices" + ] + }, + "description": "supported values for `payment.payment_type`:\n* ovo, \n* dana\n* linkaja\n* bca_va\n* bni_va\n* permata_va\n* other_va\n* alfamart\n* akulaku" + }, + "response": [] + }, + { + "name": "Credit Card without installment", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n\t\"payment\": {\n\t\t\"payment_type\": \"credit_card\"\n\t},\n\t\"customer\": {\n\t\t\"name\": \"John\",\n\t\t\"email\": \"foo@bar.com\",\n\t\t\"phone_number\": \"089922222222\"\n\t},\n\t\"items\": [{\n\t\t\"name\": \"Support Podcast\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 10000,\n\t\t\"currency\": \"IDR\"\n\t},{\n\t\t\"name\": \"Support Podcast 2\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 20000,\n\t\t\"currency\": \"IDR\"\n\t}]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8080/payment/invoices", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "payment", + "invoices" + ] + } + }, + "response": [] + }, + { + "name": "Credit Card with Installment", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n\t\"payment\": {\n\t\t\"payment_type\": \"credit_card\",\n\t\t\"credit_card\": {\n\t\t\t\"bank\": \"bca\",\n\t\t\t\"installment\": {\n\t\t\t\t\"type\": \"offline\",\n\t\t\t\t\"term\": 3\n\t\t\t}\n\t\t}\n\t},\n\t\"customer\": {\n\t\t\"name\": \"John\",\n\t\t\"email\": \"foo@bar.com\",\n\t\t\"phone_number\": \"089922222222\"\n\t},\n\t\"items\": [{\n\t\t\"name\": \"Support Podcast\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 10000,\n\t\t\"currency\": \"IDR\"\n\t},{\n\t\t\"name\": \"Support Podcast 2\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 20000,\n\t\t\"currency\": \"IDR\"\n\t}]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8080/payment/invoices", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "payment", + "invoices" + ] + }, + "description": "If you want to use installment, please provide `payment.credit_card` object.\n\n`bank` : bank providing the installment\n\n`installment.type` : either `offline` or `online`. \n\n * `offline` means cardholder bank might not be the same as the `bank`\n \n * `online` means cardholder bank should be the same as the `bank`. This useful when a particular bank is running a special promo for their cardholder.\n\n`installment.term` : the tenure for the installment" + }, + "response": [] + }, + { + "name": "Get Payment Methods", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8080/payment/methods?price=1000¤cy=IDR", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "payment", + "methods" + ], + "query": [ + { + "key": "price", + "value": "1000" + }, + { + "key": "currency", + "value": "IDR" + } + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Subscription", + "item": [ + { + "name": "Create Subscriptions", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\": \"Imre Subscription\",\n\t\"description\": \"Mantul\",\n\t\"amount\": 10000,\n\t\"user_id\": \"imre@gmail.com\",\n\t\"currency\": \"IDR\",\n\t\"total_recurrence\": 2,\n\t\"card_token\": \"\",\n\t\"charge_immediately\": true,\n\t\"schedule\": {\n\t\t\"interval\": 1,\n\t\t\"interval_unit\": \"day\",\n\t\t\"start_at\": \"2020-06-10T00:00:00.000Z\"\n\t} \n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/payment/subscriptions", + "host": [ + "{{base_url}}" + ], + "path": [ + "payment", + "subscriptions" + ] + }, + "description": "supported values for `payment.payment_type`:\n* ovo, \n* dana\n* linkaja\n* bca_va\n* bni_va\n* permata_va\n* other_va\n* alfamart\n* akulaku" + }, + "response": [] + }, + { + "name": "Pause Subscription", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{base_url}}/payment/subscriptions/{{subscription_number}}/pause", + "host": [ + "{{base_url}}" + ], + "path": [ + "payment", + "subscriptions", + "{{subscription_number}}", + "pause" + ] + }, + "description": "supported values for `payment.payment_type`:\n* ovo, \n* dana\n* linkaja\n* bca_va\n* bni_va\n* permata_va\n* other_va\n* alfamart\n* akulaku" + }, + "response": [] + }, + { + "name": "Resume Subscription", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{base_url}}/payment/subscriptions/{{subscription_number}}/resume", + "host": [ + "{{base_url}}" + ], + "path": [ + "payment", + "subscriptions", + "{{subscription_number}}", + "resume" + ] + }, + "description": "supported values for `payment.payment_type`:\n* ovo, \n* dana\n* linkaja\n* bca_va\n* bni_va\n* permata_va\n* other_va\n* alfamart\n* akulaku" + }, + "response": [] + }, + { + "name": "Stop Subscription", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{base_url}}/payment/subscriptions/{{subscription_number}}/stop", + "host": [ + "{{base_url}}" + ], + "path": [ + "payment", + "subscriptions", + "{{subscription_number}}", + "stop" + ] + }, + "description": "supported values for `payment.payment_type`:\n* ovo, \n* dana\n* linkaja\n* bca_va\n* bni_va\n* permata_va\n* other_va\n* alfamart\n* akulaku" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Callback", + "item": [ + { + "name": "Xendit Invoice Callback", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [], + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/payment/xendit/invoice/callback", + "host": [ + "{{base_url}}" + ], + "path": [ + "payment", + "xendit", + "invoice", + "callback" + ] + }, + "description": "supported values for `payment.payment_type`:\n* ovo, \n* dana\n* linkaja\n* bca_va\n* bni_va\n* permata_va\n* other_va\n* alfamart\n* akulaku" + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + } + ], + "protocolProfileBehavior": {} +} \ No newline at end of file diff --git a/docs/invoice.md b/docs/invoice.md new file mode 100644 index 0000000..bfbcac1 --- /dev/null +++ b/docs/invoice.md @@ -0,0 +1,70 @@ +Invoice API +=== + +## Generating New Invoice + +Use this endpoint to create a payment request to chosen payment channels and gateway. + +### Request + +```http +POST /payment/invoices +``` + +```json +{ + "payment": { + "payment_type": "ovo" + }, + "customer": { + "name": "John", + "email": "foo@example.com", + "phone_number": "089922222222" + }, + "items": [{ + "name": "Support Podcast", + "category": "PODCAST", + "merchant": "imrenagi.com", + "description": "donasi podcast imre nagi", + "qty": 1, + "price": 80001, + "currency": "IDR" + }] +} +``` + +> To create invoice with `credit_card` payment with/without installment, please take a look [POSTMAN COLLECTION](/example/server/go-payment.postman_collection.json) + +### Response + +When you call endpoint above, server returns all invoice data. But, to proceed to the payment page you need to pay attention to `payment` object. + +```json +{ + "payment": { + "id": 48, + "created_at": "2020-05-25T23:31:44.99873+07:00", + "updated_at": "2020-05-25T23:31:44.99873+07:00", + "deleted_at": null, + "gateway": "xendit", + "payment_type": "ovo", + "token": "", + "redirect_url": "https://invoice.xendit.co/web/invoices5ecbf2f0689543409347ec15", + "transaction_id": "5ecbf2f0689543409347ec15" + } +} +``` + +:heavy_exclamation_mark::heavy_exclamation_mark::heavy_exclamation_mark: Please note: + +### For Midtrans Payment Channel + +- Value of `payment.gateway` will is always `midtrans` +- You can use `payment.token` to open snap window by using midtrans [snap.js](https://snap-docs.midtrans.com/#snap-js) +- If you want to use [Window Redirection](https://snap-docs.midtrans.com/#window-redirection), you can open a new browser tab by using url in `payment.redirect_url` + +### For Xendit Payment Channel + +- Value of `payment.gateway` will is always `xendit` +- `payment.token` is always empty for all xendit provided payment channels +- You will always open `payment.redirect_url` in new browser tap for all payment methods provided by xendit. Including DANA, LinkAja, Kredivo, even Xendit Invoice. diff --git a/docs/methods.md b/docs/methods.md new file mode 100644 index 0000000..287fd1c --- /dev/null +++ b/docs/methods.md @@ -0,0 +1,49 @@ +Payment Methods API +=== + +## List of payment methods + +### Request + +If you want to get the estimated admin/installment fee for each payment methods, provice this GET request with optional `price` and `currency` query. Otherwise, it returns nil `admin_fee` and `installment_fee` + +```http +GET /payment/methods?price=1000¤cy=IDR +``` + +### Response + +```json +{ + "card_payment": { + "payment_type": "credit_card", + "installments": [ + { + "display_name": "", + "type": "offline", + "bank": "bca", + "terms": [ + { + "term": 0, + "admin_fee": { + "value": 2029, + "curency": "IDR" + } + } + ] + } + ] + }, + // ... redacted ... + "ewallets": [ + { + "payment_type": "gopay", + "display_name": "Gopay", + "admin_fee": { + "value": 0, + "curency": "IDR" + } + } + ] +} +``` diff --git a/docs/subscription.md b/docs/subscription.md new file mode 100644 index 0000000..da5ae00 --- /dev/null +++ b/docs/subscription.md @@ -0,0 +1,69 @@ +Subscription API +=== + +If you want to make recurring payment for your user, you can use Subscription API. For example of usage, check [Postman Collection](./go-payment.postman_collection.json) + +Per current implementation, recurring payment is only supported by Xendit gateway. Here are the recurring payment features: + +1. Supported payment method: Credit Card, Bank Transfer VA, OVO +1. User can make n times recurring payment. If you specify `0` for `total_recurrence`, it creates unlimited number of recurring payment. +1. User can `Create`, `Pause`, `Resume` and `Stop` subscription. +1. User can choose its payment method through Xendit Invoice every time they want to pay invoice. If credit card is used once, the next payment will be done automatically by charging the credit card. + +**Notes:** + +1. If user specify `card_token` in subscription create request, the card will directly be charged. You can leave it blank in order to use other payment method. + +1. If user doesnt pay the latest invoice, the next invoice will still be sent to the user on the next period. If you want to automatically stop the subscription when the invoice is not paid, please change this following line in [subscription.go](../subscription/subscription.go#L19) to + +``` + MissedPaymentAction: MissedPaymentActionStop, +``` +> This default value will be configured through configuration file later. + +## Create Subscription + + +### Request + +* You can use any integer as `schedule.interval`. +* `day`, `month` and `year` are the valid values for `schedule.interval_unit` +* If `charge_immediately` is `true`, `schedule.start_at` will be ignored and automatically set to now. +* If `charge_immediately` is `false`, `schedule.start_at` must not be empty. + +```json +POST /payment/subscriptions + +{ + "name": "Imre Subscription", + "description": "Mantul", + "amount": 10000, + "user_id": "imre@gmail.com", + "currency": "IDR", + "total_recurrence": 2, + "card_token": "", + "charge_immediately": true, + "schedule": { + "interval": 1, + "interval_unit": "day", + "start_at": "2020-06-10T00:00:00.000Z" + } +} +``` + +### Response + +If you are using xendit and charge the user immediately, `last_created_invoice` will not be empty. Use this to redirect your user to payment page. + +```json +{ + "id": 8, + "number": "9b9f1999-b2c8-4eb3-8188-23a115237a4c", + // redacted .... + // ..... + "charge_immediately": true, + "last_created_invoice": "https://invoice-staging.xendit.co/web/invoices/5edc5b024eb7c20fe6c8ae91", + "status": "ACTIVE", + "recurrence_progress": 0 +} +``` \ No newline at end of file diff --git a/example/README.md b/example/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/example/server/README.md b/example/server/README.md new file mode 100644 index 0000000..2c79c6a --- /dev/null +++ b/example/server/README.md @@ -0,0 +1,66 @@ +Example +=== + +To start using this module, you can try the example [server.go](/server.go) + +```go +package main + +import ( + "net/http" + + "github.com/gorilla/mux" + "github.com/imrenagi/go-payment/datastore/inmemory" + dsmysql "github.com/imrenagi/go-payment/datastore/mysql" + "github.com/imrenagi/go-payment/gateway/midtrans" + "github.com/imrenagi/go-payment/invoice" + "github.com/imrenagi/go-payment/manage" + "github.com/imrenagi/go-payment/server" + "github.com/imrenagi/go-payment/util/db/mysql" + "github.com/imrenagi/go-payment/util/localconfig" + "github.com/rs/cors" + "github.com/rs/zerolog/log" +) + +func main() { + + secret, err := localconfig.LoadSecret("example/server/secret.yaml") + if err != nil { + panic(err) + } + + db := mysql.NewGorm(secret.DB) + db.AutoMigrate( + &midtrans.TransactionStatus{}, + &invoice.Invoice{}, + &invoice.Payment{}, + &invoice.CreditCardDetail{}, + &invoice.LineItem{}, + &invoice.BillingAddress{}, + ) + + m := manage.NewManager(secret.Payment) + m.MustMidtransTransactionStatusRepository(dsmysql.NewMidtransTransactionRepository(db)) + m.MustInvoiceRepository(dsmysql.NewInvoiceRepository(db)) + m.MustPaymentConfigReader(inmemory.NewPaymentConfigRepository("example/server/payment-methods.yml")) + + srv := srv{ + Router: mux.NewRouter(), + paymentSrv: server.NewServer(m), + } + srv.routes() + + if err := http.ListenAndServe(":8080", srv.GetHandler()); err != nil { + log.Fatal().Msgf("Server can't run. Got: `%v`", err) + } + +} +``` + +To run the application, simply use: + +```console +$ go run example/server/server.go +``` + +> :heavy_exclamation_mark: If you want to accept payment callback from the payment gateway on your local computer for development purpose, consider to use [ngrok.io](https://ngrok.io) to expose your localhost to the internet and update the callback base URL in payment gateway dashboard and `SERVER_BASE_URL` accordingly. \ No newline at end of file diff --git a/example/server/go-payment.postman_collection.json b/example/server/go-payment.postman_collection.json deleted file mode 100644 index dee73b3..0000000 --- a/example/server/go-payment.postman_collection.json +++ /dev/null @@ -1,136 +0,0 @@ -{ - "info": { - "_postman_id": "8193a2d1-3a85-404d-a789-6e672e80d716", - "name": "go-payment", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Non Credit Card Payment", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n\t\"payment\": {\n\t\t\"payment_type\": \"alfamart\"\n\t},\n\t\"customer\": {\n\t\t\"name\": \"John\",\n\t\t\"email\": \"foo@bar.com\",\n\t\t\"phone_number\": \"\"\n\t},\n\t\"items\": [{\n\t\t\"name\": \"Support Podcast\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 10000,\n\t\t\"currency\": \"IDR\"\n\t},{\n\t\t\"name\": \"Support Podcast 2\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 20000,\n\t\t\"currency\": \"IDR\"\n\t}]\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{base_url}}/payment/invoices", - "host": [ - "{{base_url}}" - ], - "path": [ - "payment", - "invoices" - ] - }, - "description": "supported values for `payment.payment_type`:\n* ovo, \n* dana\n* linkaja\n* bca_va\n* bni_va\n* permata_va\n* other_va\n* alfamart\n* akulaku" - }, - "response": [] - }, - { - "name": "Credit Card without installment", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n\t\"payment\": {\n\t\t\"payment_type\": \"credit_card\"\n\t},\n\t\"customer\": {\n\t\t\"name\": \"John\",\n\t\t\"email\": \"foo@bar.com\",\n\t\t\"phone_number\": \"089922222222\"\n\t},\n\t\"items\": [{\n\t\t\"name\": \"Support Podcast\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 10000,\n\t\t\"currency\": \"IDR\"\n\t},{\n\t\t\"name\": \"Support Podcast 2\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 20000,\n\t\t\"currency\": \"IDR\"\n\t}]\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "localhost:8080/payment/invoices", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "payment", - "invoices" - ] - } - }, - "response": [] - }, - { - "name": "Credit Card with Installment", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n\t\"payment\": {\n\t\t\"payment_type\": \"credit_card\",\n\t\t\"credit_card\": {\n\t\t\t\"bank\": \"bca\",\n\t\t\t\"installment\": {\n\t\t\t\t\"type\": \"offline\",\n\t\t\t\t\"term\": 3\n\t\t\t}\n\t\t}\n\t},\n\t\"customer\": {\n\t\t\"name\": \"John\",\n\t\t\"email\": \"foo@bar.com\",\n\t\t\"phone_number\": \"089922222222\"\n\t},\n\t\"items\": [{\n\t\t\"name\": \"Support Podcast\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 10000,\n\t\t\"currency\": \"IDR\"\n\t},{\n\t\t\"name\": \"Support Podcast 2\",\n\t\t\"category\": \"PODCAST\",\n\t\t\"merchant\": \"imrenagi.comx\",\n\t\t\"description\": \"donasi podcast imre nagi\",\n\t\t\"qty\": 1,\n\t\t\"price\": 20000,\n\t\t\"currency\": \"IDR\"\n\t}]\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "localhost:8080/payment/invoices", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "payment", - "invoices" - ] - }, - "description": "If you want to use installment, please provide `payment.credit_card` object.\n\n`bank` : bank providing the installment\n\n`installment.type` : either `offline` or `online`. \n\n * `offline` means cardholder bank might not be the same as the `bank`\n \n * `online` means cardholder bank should be the same as the `bank`. This useful when a particular bank is running a special promo for their cardholder.\n\n`installment.term` : the tenure for the installment" - }, - "response": [] - }, - { - "name": "Get Payment Methods", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "localhost:8080/payment/methods?price=1000¤cy=IDR", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "payment", - "methods" - ], - "query": [ - { - "key": "price", - "value": "1000" - }, - { - "key": "currency", - "value": "IDR" - } - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file