From f7def175b87a49959643ef6b32145093cbf586eb Mon Sep 17 00:00:00 2001 From: Corey Fogg <84813106+cfogg-bolt@users.noreply.github.com> Date: Wed, 2 Feb 2022 10:45:07 -0800 Subject: [PATCH 1/5] Add processing initiator parsing to checkout.com integration, removed old test coverage files (#202) Add processing initiator parsing to checkout.com integration, removed old test coverage files --- coverage.txt | 14 --------- gateways/checkoutcom/checkoutcom.go | 5 ---- gateways/checkoutcom/request_builder.go | 39 +++++++++++++++++++++++-- test.sh | 12 -------- 4 files changed, 37 insertions(+), 33 deletions(-) delete mode 100644 coverage.txt delete mode 100755 test.sh diff --git a/coverage.txt b/coverage.txt deleted file mode 100644 index 141dd639..00000000 --- a/coverage.txt +++ /dev/null @@ -1,14 +0,0 @@ - -mode: atomic -mode: atomic -mode: atomic -mode: atomic -mode: atomic -mode: atomic -mode: atomic -mode: atomic -github.com/BoltApp/sleet/integration-tests/env_util.go:8.32,10.17 2 65 -github.com/BoltApp/sleet/integration-tests/env_util.go:13.2,13.10 1 65 -github.com/BoltApp/sleet/integration-tests/env_util.go:10.17,11.64 1 0 -github.com/BoltApp/sleet/integration-tests/pointers.go:3.29,5.2 1 22 -mode: atomic diff --git a/gateways/checkoutcom/checkoutcom.go b/gateways/checkoutcom/checkoutcom.go index 3c179688..7302cf36 100644 --- a/gateways/checkoutcom/checkoutcom.go +++ b/gateways/checkoutcom/checkoutcom.go @@ -1,8 +1,6 @@ package checkoutcom import ( - "encoding/json" - "fmt" "github.com/BoltApp/sleet" "github.com/BoltApp/sleet/common" "github.com/checkout/checkout-sdk-go" @@ -63,9 +61,6 @@ func (client *CheckoutComClient) Authorize(request *sleet.AuthorizationRequest) return &sleet.AuthorizationResponse{Success: false, TransactionReference: "", AvsResult: sleet.AVSResponseUnknown, CvvResult: sleet.CVVResponseUnknown, ErrorCode: err.Error()}, err } - out, _ := json.Marshal(response.StatusResponse.ResponseBody) - fmt.Printf(string(out)) - if *response.Processed.Approved { return &sleet.AuthorizationResponse{ Success: true, diff --git a/gateways/checkoutcom/request_builder.go b/gateways/checkoutcom/request_builder.go index 0852d4b1..e1e2b028 100644 --- a/gateways/checkoutcom/request_builder.go +++ b/gateways/checkoutcom/request_builder.go @@ -7,6 +7,9 @@ import ( "github.com/checkout/checkout-sdk-go/payments" ) +// Cof specifies the transaction type under the Credential-on-File framework +const recurringPaymentType = "Recurring" + func buildChargeParams(authRequest *sleet.AuthorizationRequest) (*payments.Request, error) { var source = payments.CardSource{ Type: "card", @@ -25,7 +28,7 @@ func buildChargeParams(authRequest *sleet.AuthorizationRequest) (*payments.Reque }, } - return &payments.Request{ + request := &payments.Request{ Source: source, Amount: uint64(authRequest.Amount.Amount), Capture: common.BPtr(false), @@ -35,7 +38,39 @@ func buildChargeParams(authRequest *sleet.AuthorizationRequest) (*payments.Reque Email: common.SafeStr(authRequest.BillingAddress.Email), Name: authRequest.CreditCard.FirstName + " " + authRequest.CreditCard.LastName, }, - }, nil + } + + if authRequest.ProcessingInitiator != nil { + initializeProcessingInitiator(authRequest, request, &source) + } + + return request, nil +} + +func initializeProcessingInitiator(authRequest *sleet.AuthorizationRequest, request *payments.Request, source *payments.CardSource) { + // see documentation for instructions on stored credentials, merchant-initiated transactions, and subscriptions: + // https://www.checkout.com/docs/four/payments/accept-payments/use-saved-details/about-stored-card-details + switch *authRequest.ProcessingInitiator { + // initiated by merchant or cardholder, stored card, recurring, first payment + case sleet.ProcessingInitiatorTypeInitialRecurring: + if authRequest.CreditCard.Network == sleet.CreditCardNetworkVisa { + request.PaymentType = recurringPaymentType // visa only + } + request.MerchantInitiated = common.BPtr(false) + // initiated by merchant, stored card, recurring/single transaction, follow-on payment + case sleet.ProcessingInitiatorTypeFollowingRecurring, + sleet.ProcessingInitiatorTypeStoredMerchantInitiated: + request.MerchantInitiated = common.BPtr(true) + source.Stored = common.BPtr(true) + request.PaymentType = recurringPaymentType + request.PreviousPaymentID = *authRequest.PreviousExternalTransactionID + // initiated by cardholder, stored card, single transaction, follow-on payment + case sleet.ProcessingInitiatorTypeStoredCardholderInitiated: + source.Stored = common.BPtr(true) + // initiated by merchant or cardholder, stored card, single transaction, first payment + case sleet.ProcessingInitiatorTypeInitialCardOnFile: + request.MerchantInitiated = common.BPtr(false) + } } func buildRefundParams(refundRequest *sleet.RefundRequest) (*payments.RefundsRequest, error) { diff --git a/test.sh b/test.sh deleted file mode 100755 index b074ca57..00000000 --- a/test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -for d in $(go list ./...); do - go test -race -coverprofile=profile.out -covermode=atomic $d - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done From 3671cd0e0889de29da6a4b71041df02caa2ebc12 Mon Sep 17 00:00:00 2001 From: Robert Cooper Date: Wed, 2 Feb 2022 17:47:23 -0500 Subject: [PATCH 2/5] cleanup readme + prepare for webhooks (#203) * cleanup readme + prepare for webhooks * add checkout.com --- README.md | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 38e7901a..1d619444 100644 --- a/README.md +++ b/README.md @@ -14,22 +14,40 @@ Payment abstraction library - interact with different Payment Service Providers Wherever possible, we try to use native Golang implementations of the PsP's API. We also assume that the caller can pass along raw credit card information (i.e. are PCI compliant) -### Supported API Calls +### Supported Gateway API Calls 1. Authorize 2. Capture 3. Void 4. Refund -### To run tests +### Webhooks Support -#### Unit test +We support abstracting PsP Webhook notifications into a common interface. + +### PsP Support Matrix +| PsP | Gateway APIs | Webhooks | +|-----|--------------|----------| +| [Adyen](https://docs.adyen.com/classic-integration/api-integration-ecommerce) | ✅ | ❌ | +| [Authorize.Net](https://developer.authorize.net/api/reference/index.html#payment-transactions) | ✅ | ❌ | +| [Braintree](https://www.braintreepayments.com/) | ✅ | ❌ | +| [CyberSource](https://developer.cybersource.com/api-reference-assets/index.html#payments) | ✅ | ❌ | +| [Checkout.com](https://api-reference.checkout.com/) | ✅ | ❌ | +| [FirstData](https://docs.firstdata.com/org/gateway/docs/api) | ✅ | ❌ | +| [NMI](https://secure.networkmerchants.com/gw/merchants/resources/integration/integration_portal.php#methodology) | ✅ | ❌ | +| [Orbital](https://developer.jpmorgan.com/products/orbital-api) | ✅ | ❌ | +| [RocketGate](https://www.rocketgate.com/) | ✅ | ❌ | +| [Stripe](https://stripe.com/docs/api) | ✅ | ❌ | + +## To run tests + +### Unit test ``` go test -v -tags=unit $(go list ./... | grep -v integration-tests) ``` -#### Integration test +### Integration test The following environment variables are needed in order to run tests ```shell script @@ -51,7 +69,7 @@ $ export CHECKOUTCOM_TEST_KEY="YOUR_CHECKOUTCOM_PRIVATE_KEY" Then run tests with: `go test ./integration-tests/` -#### Code Example for Auth + Capture +## Code Example for Auth + Capture ``` import ( @@ -97,13 +115,4 @@ captureRequest := sleet.CaptureRequest{ TransactionReference: authorizeResponse.TransactionReference, } client.Capture(&captureRequest) -``` - -#### Supported Gateways - -- [Authorize.Net](https://developer.authorize.net/api/reference/index.html#payment-transactions) -- [CyberSource](https://developer.cybersource.com/api-reference-assets/index.html#payments) -- [Stripe](https://stripe.com/docs/api) -- [Adyen](https://docs.adyen.com/classic-integration/api-integration-ecommerce) -- [Braintree](https://www.braintreepayments.com/) -- [NMI](https://secure.networkmerchants.com/gw/merchants/resources/integration/integration_portal.php#methodology) +``` \ No newline at end of file From fe07b333a0512fb34a2a6e6cd97e0d6d8bd84b3b Mon Sep 17 00:00:00 2001 From: Corey Fogg <84813106+cfogg-bolt@users.noreply.github.com> Date: Wed, 2 Feb 2022 15:07:17 -0800 Subject: [PATCH 3/5] Remove code coverage from circle ci step (#204) * Remove code coverage from circle ci step * cd instead of args to go test * cd instead of args to go test --- .circleci/config.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 40dc85ce..40b44406 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,9 +27,8 @@ jobs: - run: name: Integration Test command: | - go test -v \ - -coverpkg $(go list ./... | grep -v integration-tests | grep -v testing | tr '\n' ',' | sed -e 's/.$//') \ - -coverprofile=integration_coverage.profile ./integration-tests/*.go + cd integration-tests + go test -v - save_cache: key: v1-pkg-cache paths: From 573c1345197f020890ee9e112e19e2a2c5e65c28 Mon Sep 17 00:00:00 2001 From: Corey Fogg <84813106+cfogg-bolt@users.noreply.github.com> Date: Wed, 9 Feb 2022 10:57:45 -0800 Subject: [PATCH 4/5] Add nil check for MerchantOrderRef (#206) --- gateways/checkoutcom/request_builder.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/gateways/checkoutcom/request_builder.go b/gateways/checkoutcom/request_builder.go index e1e2b028..f6b54a39 100644 --- a/gateways/checkoutcom/request_builder.go +++ b/gateways/checkoutcom/request_builder.go @@ -74,17 +74,27 @@ func initializeProcessingInitiator(authRequest *sleet.AuthorizationRequest, requ } func buildRefundParams(refundRequest *sleet.RefundRequest) (*payments.RefundsRequest, error) { - return &payments.RefundsRequest{ + request := &payments.RefundsRequest{ Amount: uint64(refundRequest.Amount.Amount), - Reference: *refundRequest.MerchantOrderReference, - }, nil + } + + if refundRequest.MerchantOrderReference != nil { + request.Reference = *refundRequest.MerchantOrderReference + } + + return request, nil } func buildCaptureParams(captureRequest *sleet.CaptureRequest) (*payments.CapturesRequest, error) { - return &payments.CapturesRequest{ + request := &payments.CapturesRequest{ Amount: uint64(captureRequest.Amount.Amount), - Reference: *captureRequest.MerchantOrderReference, - }, nil + } + + if captureRequest.MerchantOrderReference != nil { + request.Reference = *captureRequest.MerchantOrderReference + } + + return request, nil } func buildVoidParams(voidRequest *sleet.VoidRequest) (*payments.VoidsRequest, error) { From 689a60c53d8851302c59ce6a45dabf807d9e5338 Mon Sep 17 00:00:00 2001 From: Vinay Nagar <58158648+UnitRoot22@users.noreply.github.com> Date: Thu, 17 Feb 2022 12:46:54 -0500 Subject: [PATCH 5/5] Fix cases when amount is less 10 cents for NMI (#207) Co-authored-by: Vinayaka Nagar --- gateways/nmi/request_builders.go | 9 +++++---- go.mod | 1 + go.sum | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/gateways/nmi/request_builders.go b/gateways/nmi/request_builders.go index 22a28312..507a8b68 100644 --- a/gateways/nmi/request_builders.go +++ b/gateways/nmi/request_builders.go @@ -2,8 +2,10 @@ package nmi import ( "fmt" - "github.com/BoltApp/sleet" "strconv" + + "github.com/BoltApp/sleet" + "github.com/shopspring/decimal" ) // NMI transaction types @@ -85,7 +87,6 @@ func enableTestMode(testMode bool) *string { } func formatAmount(amountInt int64) *string { - amountString := strconv.FormatInt(amountInt, 10) - formattedAmount := fmt.Sprintf("%s.%s", amountString[:len(amountString)-2], amountString[len(amountString)-2:]) - return &formattedAmount + formattatedAmount := decimal.NewFromInt(amountInt).Div(decimal.NewFromInt(int64(100))).StringFixed(2) + return &formattatedAmount } diff --git a/go.mod b/go.mod index 0ff93297..e74e0799 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/google/go-cmp v0.5.1 github.com/jarcoal/httpmock v1.0.5 github.com/rocketgate/rocketgate-go-sdk v0.0.0-20220106233346-17d98d87a0ff + github.com/shopspring/decimal v1.3.1 // indirect github.com/stripe/stripe-go v70.11.0+incompatible golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 // indirect diff --git a/go.sum b/go.sum index 22b31666..ba805da3 100644 --- a/go.sum +++ b/go.sum @@ -133,6 +133,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/rocketgate/rocketgate-go-sdk v0.0.0-20220106233346-17d98d87a0ff h1:KcXYk9nBcMz35HTctRxrTziAjSJUk1Lid5WVaEMjU6w= github.com/rocketgate/rocketgate-go-sdk v0.0.0-20220106233346-17d98d87a0ff/go.mod h1:5zfoDg5zWlwcB1SlaOLYa2Hm9YJaQlB7fqccBb6tosg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=