Skip to content

Commit

Permalink
Fix account holder payment context source. Partial Authorization (#120)
Browse files Browse the repository at this point in the history
* Add partial authorization in payment request

* Fix account holder payment context source
  • Loading branch information
armando-rodriguez-cko authored Aug 2, 2024
1 parent 0bfe87e commit 83554ee
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 47 deletions.
5 changes: 3 additions & 2 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,12 @@ type (
AccountHolder struct {
Type AccountHolderType `json:"type,omitempty"`
Title string `json:"title,omitempty"`
FullName string `json:"full_name,omitempty"`
FirstName string `json:"first_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
LastName string `json:"last_name,omitempty"`
Email string `json:"email,omitempty"`
Gender string `json:"gender,omitempty"`
CompanyName string `json:"company_name,omitempty"`
TaxId string `json:"tax_id,omitempty"`
DateOfBirth string `json:"date_of_birth,omitempty"`
Expand All @@ -193,8 +196,6 @@ type (
BillingAddress *Address `json:"billing_address,omitempty"`
Phone *Phone `json:"phone,omitempty"`
Identification *AccountHolderIdentification `json:"identification,omitempty"`
Email string `json:"email,omitempty"`
Gender string `json:"gender,omitempty"`
}
)

Expand Down
67 changes: 36 additions & 31 deletions payments/nas/payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,42 +77,47 @@ type (
IssuingCountry common.Country `json:"issuing_country,omitempty"`
DateOfExpiry string `json:"date_of_expiry,omitempty"`
}

PartialAuthorization struct {
Enabled bool `json:"enabled,omitempty"`
}
)

// Request
type (
PaymentRequest struct {
PaymentContextId string `json:"payment_context_id,omitempty"`
Source payments.PaymentSource `json:"source,omitempty"`
Amount int64 `json:"amount,omitempty"`
Currency common.Currency `json:"currency,omitempty"`
PaymentType payments.PaymentType `json:"payment_type,omitempty"`
MerchantInitiated bool `json:"merchant_initiated"`
Reference string `json:"reference,omitempty"`
Description string `json:"description,omitempty"`
AuthorizationType AuthorizationType `json:"authorization_type,omitempty"`
Capture bool `json:"capture"`
CaptureOn *time.Time `json:"capture_on,omitempty"`
Customer *common.CustomerRequest `json:"customer,omitempty"`
BillingDescriptor *payments.BillingDescriptor `json:"billing_descriptor,omitempty"`
ShippingDetails *payments.ShippingDetails `json:"shipping,omitempty"`
Segment *payments.PaymentSegment `json:"segment,omitempty"`
ThreeDsRequest *payments.ThreeDsRequest `json:"3ds,omitempty"`
PreviousPaymentId string `json:"previous_payment_id,omitempty"`
ProcessingChannelId string `json:"processing_channel_id,omitempty"`
Risk *payments.RiskRequest `json:"risk,omitempty"`
SuccessUrl string `json:"success_url,omitempty"`
FailureUrl string `json:"failure_url,omitempty"`
PaymentIp string `json:"payment_ip,omitempty"`
Sender Sender `json:"sender,omitempty"`
Recipient *payments.PaymentRecipient `json:"recipient,omitempty"`
Marketplace *common.MarketplaceData `json:"marketplace,omitempty"`
AmountAllocations []common.AmountAllocations `json:"amount_allocations,omitempty"`
Processing *payments.ProcessingSettings `json:"processing,omitempty"`
Items []payments.Product `json:"items,omitempty"`
Retry *payments.PaymentRetryRequest `json:"retry,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
Instruction *PaymentInstruction `json:"instruction,omitempty"`
PaymentContextId string `json:"payment_context_id,omitempty"`
Source payments.PaymentSource `json:"source,omitempty"`
Amount int64 `json:"amount,omitempty"`
Currency common.Currency `json:"currency,omitempty"`
PaymentType payments.PaymentType `json:"payment_type,omitempty"`
MerchantInitiated bool `json:"merchant_initiated"`
Reference string `json:"reference,omitempty"`
Description string `json:"description,omitempty"`
AuthorizationType AuthorizationType `json:"authorization_type,omitempty"`
PartialAuthorization *PartialAuthorization `json:"partial_authorization,omitempty"`
Capture bool `json:"capture"`
CaptureOn *time.Time `json:"capture_on,omitempty"`
Customer *common.CustomerRequest `json:"customer,omitempty"`
BillingDescriptor *payments.BillingDescriptor `json:"billing_descriptor,omitempty"`
ShippingDetails *payments.ShippingDetails `json:"shipping,omitempty"`
Segment *payments.PaymentSegment `json:"segment,omitempty"`
ThreeDsRequest *payments.ThreeDsRequest `json:"3ds,omitempty"`
PreviousPaymentId string `json:"previous_payment_id,omitempty"`
ProcessingChannelId string `json:"processing_channel_id,omitempty"`
Risk *payments.RiskRequest `json:"risk,omitempty"`
SuccessUrl string `json:"success_url,omitempty"`
FailureUrl string `json:"failure_url,omitempty"`
PaymentIp string `json:"payment_ip,omitempty"`
Sender Sender `json:"sender,omitempty"`
Recipient *payments.PaymentRecipient `json:"recipient,omitempty"`
Marketplace *common.MarketplaceData `json:"marketplace,omitempty"`
AmountAllocations []common.AmountAllocations `json:"amount_allocations,omitempty"`
Processing *payments.ProcessingSettings `json:"processing,omitempty"`
Items []payments.Product `json:"items,omitempty"`
Retry *payments.PaymentRetryRequest `json:"retry,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
Instruction *PaymentInstruction `json:"instruction,omitempty"`
}

PayoutRequest struct {
Expand Down
3 changes: 2 additions & 1 deletion payments/nas/sources/contexts/contexts.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ type (
}

requestPaymentContextsPayPalSource struct {
Type payments.SourceType `json:"type,omitempty"`
Type payments.SourceType `json:"type,omitempty"`
AccountHolder *common.AccountHolder `json:"account_holder,omitempty"`
}
)

Expand Down
41 changes: 41 additions & 0 deletions test/deserializer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,44 @@ func TestControlsUnmarshallJson(t *testing.T) {
})
}
}

func TestPaymentContextUnmarshallJson(t *testing.T) {
paypalResponse, _ := ioutil.ReadFile("resources/payment_context_paypal_details_response.json")

cases := []struct {
name string
json []byte
checker func(*bytes.Buffer, error)
}{
{
name: "when deserializing payment_context_paypal_details_response type must be PayPal",
json: paypalResponse,
checker: func(serialized *bytes.Buffer, err error) {
assert.Nil(t, err)
assert.NotNil(t, serialized)

var deserialized map[string]interface{}
unmErr := json.Unmarshal(serialized.Bytes(), &deserialized)

assert.Nil(t, unmErr)
assert.NotNil(t, deserialized["payment_request"])
paymentRequest := deserialized["payment_request"].(map[string]interface{})
source := paymentRequest["source"].(map[string]interface{})

assert.Equal(t, "paypal", source["type"])
assert.Contains(t, source, "type")
assert.Contains(t, source, "account_holder")

accountHolder := source["account_holder"].(map[string]interface{})
assert.Contains(t, accountHolder, "full_name")
assert.Equal(t, "Andrey Young", accountHolder["full_name"])
},
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
tc.checker(bytes.NewBuffer(tc.json), nil)
})
}
}
104 changes: 91 additions & 13 deletions test/payments_increment_authorization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"

"github.com/checkout/checkout-sdk-go/common"
"github.com/checkout/checkout-sdk-go/payments"
"github.com/checkout/checkout-sdk-go/payments/nas"
"github.com/checkout/checkout-sdk-go/payments/nas/sources"
)

func TestIncrementAuthorization(t *testing.T) {
t.Skip("Skipping because increment authorization needs an authorized payment")
paymentResponse := makeCardPayment(t, false, 10)
paymentResponse := makeCardPaymentPartialAuthorization(t, false, 10)
assert.NotNil(t, paymentResponse, "Expected paymentResponse not to be nil")

metadata := make(map[string]interface{})
metadata["TestIncrementAuthorization"] = "metadata"
Expand All @@ -38,20 +40,26 @@ func TestIncrementAuthorization(t *testing.T) {
checkerOne: func(response *nas.IncrementAuthorizationResponse, err error) {
assert.Nil(t, err)
assert.NotNil(t, response)
assert.NotEmpty(t, response.Reference)
assert.Equal(t, int64(5), response.Amount)
assert.NotEmpty(t, response.ActionId)
assert.NotEmpty(t, response.Currency)
assert.False(t, response.Approved)
assert.NotEmpty(t, response.ResponseCode)
assert.NotEmpty(t, response.ResponseSummary)
assert.NotEmpty(t, response.ExpiresOn)
assert.NotEmpty(t, response.ProcessedOn)
assert.NotEmpty(t, response.Balances)
assert.NotEmpty(t, response.Links)
assert.NotEmpty(t, response.Links["payment"])
},
checkerTwo: func(response *nas.GetPaymentResponse, err error) {
assert.NotEmpty(t, response.Balances)
assert.Equal(t, int64(10), response.Balances.TotalAuthorized)
assert.Equal(t, int64(5), response.Balances.TotalCaptured)
assert.Equal(t, int64(0), response.Balances.TotalCaptured)
assert.Equal(t, int64(0), response.Balances.TotalRefunded)
assert.Equal(t, int64(0), response.Balances.TotalVoided)
assert.Equal(t, int64(0), response.Balances.AvailableToCapture)
assert.Equal(t, int64(5), response.Balances.AvailableToRefund)
assert.Equal(t, int64(0), response.Balances.AvailableToVoid)
assert.Equal(t, int64(10), response.Balances.AvailableToCapture)
assert.Equal(t, int64(0), response.Balances.AvailableToRefund)
assert.Equal(t, int64(10), response.Balances.AvailableToVoid)
},
},
}
Expand All @@ -69,8 +77,8 @@ func TestIncrementAuthorization(t *testing.T) {
}

func TestIncrementAuthorizationIdempotently(t *testing.T) {
t.Skip("Skipping because increment authorization needs an authorized payment")
paymentResponse := makeCardPayment(t, false, 10)
paymentResponse := makeCardPaymentPartialAuthorization(t, false, 10)
assert.NotNil(t, paymentResponse, "Expected paymentResponse not to be nil")

metadata := make(map[string]interface{})
metadata["TestIncrementAuthorization"] = "metadata"
Expand Down Expand Up @@ -115,7 +123,9 @@ func TestIncrementAuthorizationIdempotently(t *testing.T) {
checker: func(response1 interface{}, err1 error, response2 interface{}, err2 error) {
assert.Nil(t, err1)
assert.NotNil(t, response1)
assert.NotNil(t, err2)
assert.Nil(t, err2)
assert.NotNil(t, response2)
assert.NotEqual(t, response1.(*nas.IncrementAuthorizationResponse).ActionId, response2.(*nas.IncrementAuthorizationResponse).ActionId)
},
},
}
Expand All @@ -128,15 +138,15 @@ func TestIncrementAuthorizationIdempotently(t *testing.T) {
return client.IncrementAuthorization(tc.paymentId, tc.request, &tc.idempotencyKeyRandom1)
}
predicateOne := func(data interface{}) bool {
response := data.(*payments.CaptureResponse)
response := data.(*nas.IncrementAuthorizationResponse)
return response.Links != nil && len(response.Links) >= 0
}

processTwo := func() (interface{}, error) {
return client.IncrementAuthorization(tc.paymentId, tc.request, &tc.idempotencyKeyRandom2)
}
predicateTwo := func(data interface{}) bool {
response := data.(*payments.CaptureResponse)
response := data.(*nas.IncrementAuthorizationResponse)
return response.Links != nil && len(response.Links) >= 0
}

Expand All @@ -146,3 +156,71 @@ func TestIncrementAuthorizationIdempotently(t *testing.T) {
})
}
}

func makeCardPaymentPartialAuthorization(t *testing.T, shouldCapture bool, amount int64) *nas.PaymentResponse {
currentYear := time.Now().Year() + 1

cardSource := sources.NewRequestCardSource()
cardSource.Name = "Mr. Test"
cardSource.Number = "4556447238607884"
cardSource.ExpiryYear = currentYear
cardSource.ExpiryMonth = 12
cardSource.Cvv = "123"
cardSource.BillingAddress = &common.Address{
AddressLine1: "CheckoutSdk.com",
AddressLine2: "90 Tottenham Court Road",
City: "London",
State: "London",
Zip: "W1T 4TJ",
Country: common.GB,
}
cardSource.Phone = &common.Phone{
CountryCode: "44",
Number: "1234567890",
}

customerRequest := &common.CustomerRequest{
Email: "[email protected]",
Name: "Test Customer",
Phone: &common.Phone{
CountryCode: "44",
Number: "1234567890",
},
}

paymentIndividualSender := nas.NewRequestIndividualSender()
paymentIndividualSender.FirstName = "Mr"
paymentIndividualSender.LastName = "Test"
paymentIndividualSender.Address = &common.Address{
AddressLine1: "CheckoutSdk.com",
AddressLine2: "90 Tottenham Court Road",
City: "London",
State: "London",
Zip: "W1T 4TJ",
Country: common.GB,
}

paymentRequest := nas.PaymentRequest{
Source: cardSource,
Amount: amount,
Currency: common.USD,
Reference: uuid.New().String(),
Description: "Test Payment",
Capture: shouldCapture,
Customer: customerRequest,
Sender: paymentIndividualSender,
AuthorizationType: nas.EstimatedAuthorizationType,
PartialAuthorization: &nas.PartialAuthorization{
Enabled: true,
},
BillingDescriptor: &payments.BillingDescriptor{
Name: "CheckoutSdk.com",
City: "London",
},
}

response, err := DefaultApi().Payments.RequestPayment(paymentRequest, nil)
assert.Nil(t, err, "Expected no error in RequestPayment")
assert.NotNil(t, response, "Expected response not to be nil")
return response
}
50 changes: 50 additions & 0 deletions test/resources/payment_context_paypal_details_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"partner_metadata": {
"customer_id": "EWR4HCRWOEIW4",
"order_id": "NF89G5J9E5G905E90"
},
"payment_request": {
"amount": 2500,
"authorization_type": "Final",
"capture": true,
"currency": "GBP",
"failure_url": "https://url/payment-failed",
"items": [
{
"discount_amount": 0,
"name": "£25.00 Voucher",
"quantity": 1,
"tax_amount": 0,
"total_amount": 2500,
"unit_price": 2500
}
],
"payment_type": "Regular",
"processing": {
"shipping_amount": 0,
"user_action": "PAY_NOW"
},
"processing_channel_id": "pc_strugfrty47ellyymdfg6fzhc4i",
"reference": "543454",
"shipping": {
"address": {
"address_line1": "Main Address",
"address_line2": "Flor 1",
"city": "London",
"country": "GB",
"state": "State",
"zip": "ZIP"
},
"first_name": "Andrey Young"
},
"source": {
"account_holder": {
"email": "[email protected]",
"full_name": "Andrey Young"
},
"type": "paypal"
},
"success_url": "https://url/payment-successful"
},
"status": "Created"
}

0 comments on commit 83554ee

Please sign in to comment.