This repository has been archived by the owner on Apr 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/master'
- Loading branch information
Showing
10 changed files
with
511 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package checkoutcom | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/BoltApp/sleet" | ||
"github.com/BoltApp/sleet/common" | ||
"github.com/checkout/checkout-sdk-go" | ||
"github.com/checkout/checkout-sdk-go/payments" | ||
"net/http" | ||
"strconv" | ||
) | ||
|
||
// checkout.com documentation here: https://www.checkout.com/docs/four/payments/accept-payments, SDK here: https://github.com/checkout/checkout-sdk-go | ||
|
||
// checkoutomClient uses API-Key and custom http client to make http calls | ||
type CheckoutComClient struct { | ||
apiKey string | ||
httpClient *http.Client | ||
} | ||
|
||
const AcceptedStatusCode = 202 | ||
|
||
// NewClient creates a CheckoutComClient | ||
// Note: the environment is indicated by the apiKey. See "isSandbox" assignment in checkout.Create. | ||
func NewClient(apiKey string) *CheckoutComClient { | ||
return NewWithHTTPClient(apiKey, common.DefaultHttpClient()) | ||
} | ||
|
||
// NewWithHTTPClient uses a custom http client for requests | ||
func NewWithHTTPClient(apiKey string, httpClient *http.Client) *CheckoutComClient { | ||
return &CheckoutComClient{ | ||
apiKey: apiKey, | ||
httpClient: httpClient, | ||
} | ||
} | ||
|
||
func (client *CheckoutComClient) generateCheckoutDCClient() (*payments.Client, error) { | ||
config, err := checkout.Create(client.apiKey, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
config.HTTPClient = client.httpClient | ||
|
||
return payments.NewClient(*config), nil | ||
} | ||
|
||
// Authorize a transaction for specified amount | ||
func (client *CheckoutComClient) Authorize(request *sleet.AuthorizationRequest) (*sleet.AuthorizationResponse, error) { | ||
checkoutComClient, err := client.generateCheckoutDCClient() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
input, err := buildChargeParams(request) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
response, err := checkoutComClient.Request(input, nil) | ||
|
||
if err != nil { | ||
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, | ||
TransactionReference: response.Processed.ID, | ||
AvsResult: sleet.AVSresponseZipMatchAddressMatch, // TODO: Use translateAvs(AVSResponseCode(response.Processed.Source.AVSCheck)) to enable avs code handling | ||
CvvResult: sleet.CVVResponseMatch, // TODO: use translateCvv(CVVResponseCode(response.Processed.Source.CVVCheck)) to enable cvv code handling | ||
AvsResultRaw: response.Processed.Source.AVSCheck, | ||
CvvResultRaw: response.Processed.Source.CVVCheck, | ||
Response: response.Processed.ResponseCode, | ||
}, nil | ||
} else { | ||
return &sleet.AuthorizationResponse{ | ||
Success: false, | ||
TransactionReference: "", | ||
AvsResult: sleet.AVSResponseUnknown, | ||
CvvResult: sleet.CVVResponseUnknown, | ||
Response: response.Processed.ResponseCode, | ||
ErrorCode: response.Processed.ResponseCode, | ||
}, nil | ||
} | ||
} | ||
|
||
// Capture an authorized transaction by charge ID | ||
func (client *CheckoutComClient) Capture(request *sleet.CaptureRequest) (*sleet.CaptureResponse, error) { | ||
checkoutComClient, err := client.generateCheckoutDCClient() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
input, err := buildCaptureParams(request) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
response, err := checkoutComClient.Captures(request.TransactionReference, input, nil) | ||
|
||
if err != nil { | ||
return &sleet.CaptureResponse{Success: false, ErrorCode: common.SPtr(err.Error())}, err | ||
} | ||
|
||
if response.StatusResponse.StatusCode == AcceptedStatusCode { | ||
return &sleet.CaptureResponse{Success: true, TransactionReference: request.TransactionReference}, nil | ||
} else { | ||
return &sleet.CaptureResponse{ | ||
Success: false, | ||
ErrorCode: common.SPtr(strconv.Itoa(response.StatusResponse.StatusCode)), | ||
TransactionReference: request.TransactionReference, | ||
}, nil | ||
} | ||
} | ||
|
||
// Refund a captured transaction with amount and charge ID | ||
func (client *CheckoutComClient) Refund(request *sleet.RefundRequest) (*sleet.RefundResponse, error) { | ||
config, err := checkout.Create(client.apiKey, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
config.HTTPClient = client.httpClient | ||
|
||
checkoutComClient := payments.NewClient(*config) | ||
|
||
input, err := buildRefundParams(request) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
response, err := checkoutComClient.Refunds(request.TransactionReference, input, nil) | ||
if err != nil { | ||
return &sleet.RefundResponse{Success: false, ErrorCode: common.SPtr(err.Error())}, err | ||
} | ||
|
||
if response.StatusResponse.StatusCode == AcceptedStatusCode { | ||
return &sleet.RefundResponse{Success: true, TransactionReference: response.Accepted.Reference}, nil | ||
} else { | ||
return &sleet.RefundResponse{ | ||
Success: false, | ||
ErrorCode: common.SPtr(strconv.Itoa(response.StatusResponse.StatusCode)), | ||
TransactionReference: request.TransactionReference, | ||
}, nil | ||
} | ||
} | ||
|
||
// Void an authorized transaction with charge ID | ||
func (client *CheckoutComClient) Void(request *sleet.VoidRequest) (*sleet.VoidResponse, error) { | ||
checkoutComClient, err := client.generateCheckoutDCClient() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
input, err := buildVoidParams(request) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
response, err := checkoutComClient.Voids(request.TransactionReference, input, nil) | ||
|
||
if err != nil { | ||
return &sleet.VoidResponse{Success: false, ErrorCode: common.SPtr(err.Error())}, err | ||
} | ||
|
||
if response.StatusResponse.StatusCode == AcceptedStatusCode { | ||
return &sleet.VoidResponse{Success: true, TransactionReference: response.Accepted.Reference}, nil | ||
} else { | ||
return &sleet.VoidResponse{ | ||
Success: false, | ||
ErrorCode: common.SPtr(strconv.Itoa(response.StatusResponse.StatusCode)), | ||
TransactionReference: request.TransactionReference, | ||
}, nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package checkoutcom | ||
|
||
import ( | ||
"github.com/BoltApp/sleet" | ||
"github.com/BoltApp/sleet/common" | ||
checkout_com_common "github.com/checkout/checkout-sdk-go/common" | ||
"github.com/checkout/checkout-sdk-go/payments" | ||
) | ||
|
||
func buildChargeParams(authRequest *sleet.AuthorizationRequest) (*payments.Request, error) { | ||
var source = payments.CardSource{ | ||
Type: "card", | ||
Number: authRequest.CreditCard.Number, | ||
ExpiryMonth: uint64(authRequest.CreditCard.ExpirationMonth), | ||
ExpiryYear: uint64(authRequest.CreditCard.ExpirationYear), | ||
Name: authRequest.CreditCard.FirstName + " " + authRequest.CreditCard.LastName, | ||
CVV: authRequest.CreditCard.CVV, | ||
BillingAddress: &checkout_com_common.Address{ | ||
AddressLine1: common.SafeStr(authRequest.BillingAddress.StreetAddress1), | ||
AddressLine2: common.SafeStr(authRequest.BillingAddress.StreetAddress2), | ||
City: common.SafeStr(authRequest.BillingAddress.Locality), | ||
State: common.SafeStr(authRequest.BillingAddress.RegionCode), | ||
ZIP: common.SafeStr(authRequest.BillingAddress.PostalCode), | ||
Country: common.SafeStr(authRequest.BillingAddress.CountryCode), | ||
}, | ||
} | ||
|
||
return &payments.Request{ | ||
Source: source, | ||
Amount: uint64(authRequest.Amount.Amount), | ||
Capture: common.BPtr(false), | ||
Currency: authRequest.Amount.Currency, | ||
Reference: authRequest.MerchantOrderReference, | ||
Customer: &payments.Customer{ | ||
Email: common.SafeStr(authRequest.BillingAddress.Email), | ||
Name: authRequest.CreditCard.FirstName + " " + authRequest.CreditCard.LastName, | ||
}, | ||
}, nil | ||
} | ||
|
||
func buildRefundParams(refundRequest *sleet.RefundRequest) (*payments.RefundsRequest, error) { | ||
return &payments.RefundsRequest{ | ||
Amount: uint64(refundRequest.Amount.Amount), | ||
Reference: *refundRequest.MerchantOrderReference, | ||
}, nil | ||
} | ||
|
||
func buildCaptureParams(captureRequest *sleet.CaptureRequest) (*payments.CapturesRequest, error) { | ||
return &payments.CapturesRequest{ | ||
Amount: uint64(captureRequest.Amount.Amount), | ||
Reference: *captureRequest.MerchantOrderReference, | ||
}, nil | ||
} | ||
|
||
func buildVoidParams(voidRequest *sleet.VoidRequest) (*payments.VoidsRequest, error) { | ||
return &payments.VoidsRequest{ | ||
Reference: *voidRequest.MerchantOrderReference, | ||
}, nil | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package checkoutcom | ||
|
||
import "github.com/BoltApp/sleet" | ||
|
||
var cvvMap = map[CVVResponseCode]sleet.CVVResponse{ | ||
CVVResponseMatched: sleet.CVVResponseMatch, | ||
CVVResponseNotConfigured: sleet.CVVResponseError, | ||
CVVResponseCVDMissing: sleet.CVVResponseError, | ||
CVVResponseNotPresent: sleet.CVVResponseRequiredButMissing, | ||
CVVResponseNotValid: sleet.CVVResponseSkipped, | ||
CVVResponseFailed: sleet.CVVResponseError, | ||
} | ||
|
||
func translateCvv(code CVVResponseCode) sleet.CVVResponse { | ||
sleetCode, ok := cvvMap[code] | ||
if !ok { | ||
return sleet.CVVResponseUnknown | ||
} | ||
return sleetCode | ||
} | ||
|
||
var avsMap = map[AVSResponseCode]sleet.AVSResponse{ | ||
AVSResponseStreetMatch: sleet.AVSResponseMatch, | ||
AVSResponseStreetMatchPostalUnverified: sleet.AVSResponseNonUsZipUnverifiedAddressMatch, | ||
AVSResponseStreetAndPostalUnverified: sleet.AVSResponseNonUsZipNoMatchAddressNoMatch, | ||
AVSResponseStreetAndPostalMatch: sleet.AVSResponseNameMatchZipMatchAddressMatch, | ||
AVSResponseAddressMatchError: sleet.AVSResponseError, | ||
AVSResponseStreetAndPostalMatchUK: sleet.AVSResponseNonUsZipMatchAddressMatch, | ||
AVSResponseNotVerifiedOrNotSupported: sleet.AVSResponseUnsupported, | ||
AVSResponseAddressUnverified: sleet.AVSResponseNonUsZipNoMatchAddressNoMatch, | ||
AVSResponseStreetAndPostalMatchMIntl: sleet.AVSResponseNonUsZipMatchAddressMatch, | ||
AVSResponseNoAddressMatch: sleet.AVSResponseNoMatch, | ||
AVSResponseAVSNotRequested: sleet.AVSResponseSkipped, | ||
AVSResponseStreetUnverifiedPostalMatch: sleet.AVSResponseZipMatchAddressUnverified, | ||
AVSResponseAVSUnavailable: sleet.AVSResponseError, | ||
AVSResponseAVSUnsupported: sleet.AVSResponseUnsupported, | ||
AVSResponseMatchNotCapable: sleet.AVSResponseError, | ||
AVSResponseNineDigitPostalMatch: sleet.AVSResponseZip9MatchAddressNoMatch, | ||
AVSResponseStreetAndNineDigitPostalMatch: sleet.AVSResponseZip9MatchAddressMatch, | ||
AVSResponseStreetAndFiveDigitPostalMatch: sleet.AVSResponseZip5MatchAddressMatch, | ||
AVSResponseFiveDigitPostalMatch: sleet.AVSResponseZip5MatchAddressNoMatch, | ||
AVSResponseCardholderNameIncorrectPostalMatch: sleet.AVSResponseNameNoMatchZipMatch, | ||
AVSResponseCardholderNameIncorrectStreetAndPostalMatch: sleet.AVSResponseNameMatchZipMatchAddressMatch, | ||
AVSResponseCardholderNameIncorrectStreetMatch: sleet.AVSResponseNameMatchZipNoMatchAddressMatch, | ||
AVSResponseCardholderNameMatch: sleet.AVSResponseNameMatchZipNoMatchAddressNoMatch, | ||
AVSResponseCardholderNameAndPostalMatch: sleet.AVSResponseNameMatchZipMatchAddressNoMatch, | ||
AVSResponseCardholderNameAndStreetAndPostalMatch: sleet.AVSResponseNameMatchZipMatchAddressMatch, | ||
AVSResponseCardholderNameAndStreetMatch: sleet.AVSResponseNameMatchZipNoMatchAddressMatch, | ||
} | ||
|
||
func translateAvs(avs AVSResponseCode) sleet.AVSResponse { | ||
sleetCode, ok := avsMap[avs] | ||
if !ok { | ||
return sleet.AVSResponseUnknown | ||
} | ||
return sleetCode | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package checkoutcom | ||
|
||
type CVVResponseCode string | ||
|
||
// See https://www.checkout.com/docs/resources/codes/cvv-response-codes | ||
const ( | ||
CVVResponseNotPresent CVVResponseCode = "X" | ||
CVVResponseNotConfigured CVVResponseCode = "U" | ||
CVVResponseCVDMissing CVVResponseCode = "P" | ||
CVVResponseMatched CVVResponseCode = "Y" | ||
CVVResponseNotValid CVVResponseCode = "D" | ||
CVVResponseFailed CVVResponseCode = "N" | ||
) | ||
|
||
type AVSResponseCode string | ||
|
||
// See https://www.checkout.com/docs/resources/codes/avs-codes | ||
const ( | ||
AVSResponseStreetMatch AVSResponseCode = "A" | ||
AVSResponseStreetMatchPostalUnverified AVSResponseCode = "B" | ||
AVSResponseStreetAndPostalUnverified AVSResponseCode = "C" | ||
AVSResponseStreetAndPostalMatch AVSResponseCode = "D" | ||
AVSResponseAddressMatchError AVSResponseCode = "E" | ||
AVSResponseStreetAndPostalMatchUK AVSResponseCode = "F" | ||
AVSResponseNotVerifiedOrNotSupported AVSResponseCode = "G" | ||
AVSResponseAddressUnverified AVSResponseCode = "I" | ||
AVSResponseStreetAndPostalMatchMIntl AVSResponseCode = "M" | ||
AVSResponseNoAddressMatch AVSResponseCode = "N" | ||
AVSResponseAVSNotRequested AVSResponseCode = "O" | ||
AVSResponseStreetUnverifiedPostalMatch AVSResponseCode = "P" | ||
AVSResponseAVSUnavailable AVSResponseCode = "R" | ||
AVSResponseAVSUnsupported AVSResponseCode = "S" | ||
AVSResponseMatchNotCapable AVSResponseCode = "U" | ||
AVSResponseNineDigitPostalMatch AVSResponseCode = "W" | ||
AVSResponseStreetAndNineDigitPostalMatch AVSResponseCode = "X" | ||
AVSResponseStreetAndFiveDigitPostalMatch AVSResponseCode = "Y" | ||
AVSResponseFiveDigitPostalMatch AVSResponseCode = "Z" | ||
AVSResponseCardholderNameIncorrectPostalMatch AVSResponseCode = "AE1" | ||
AVSResponseCardholderNameIncorrectStreetAndPostalMatch AVSResponseCode = "AE2" | ||
AVSResponseCardholderNameIncorrectStreetMatch AVSResponseCode = "AE3" | ||
AVSResponseCardholderNameMatch AVSResponseCode = "AE4" | ||
AVSResponseCardholderNameAndPostalMatch AVSResponseCode = "AE5" | ||
AVSResponseCardholderNameAndStreetAndPostalMatch AVSResponseCode = "AE6" | ||
AVSResponseCardholderNameAndStreetMatch AVSResponseCode = "AE7" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.