We use cookies to improve your experience on our website. By browsing this website, you agree to our use of cookies. More info Ok, I understand!

19 January 2016

Go-client for PayPal API

Go-client for PayPal API

Hey! We have developed the service for log collection, transfer and analysis, which has Go backend. In this article we are going to tell you about the issue that we faced while trying to connect our project to PayPal system and we’ll also tell you about the solution that we found and successfully integrated.

Most people have experience in working with PayPal APl. Using OAuth 2.0 is quite easy: connect library-Client to your project and start implementation.

There are official SDK libraries for PHP, Java and Python, but our service is written in GO, and in this case SDK search mostly fails (https://github.com/search?q=paypal+golang). As a result we have found five projects on github, two of which seem worthily, but at the same time they have bounded functional:

  • leebenson/paypal (API incomplete cover)
  • crowdmob/paypal (implement only Express Checkout)

OAuth 2.0

At the stage of the development we used sandbox, where we tested all kinds of APl requests.

First stage is working with PayPal protocol and authorization. PayPal uses OAuth 2.0. First we have to receive secure keys (client_id and secret_key).

Authorization is accomplished like this: after receiving client_id and secret_key, you need to make a request to PayPal for getting access_token, which is valid during certain period of time. After that all requests to PayPal should be followed by this access_token in the request message header (-u "<clientId>:<secret>").

PayPal OAuth scheme

Using our Client when working with PayPal APl.

import "github.com/logpacker/PayPal-Go-SDK"
// ...
// Create a client instance
c, err := paypalsdk.NewClient("clientID", "secretID", paypalsdk.APIBaseSandBox)
accessToken, err := c.GetAccessToken()

Next the Client’s object will have all verifiable methods for using APl. E.g., for creating a payment, you need the following command:

paymentResponse, err := client.CreatePayment(p)

We work on providing and describing all APl verifiable operations, meanwhile there is a possibility to call any enclosed method by using basic functions:

req, err := c.NewRequest(method, url, payload)
c.SendWithAuth(req, &resp)

All requests to PayPal can be logged, the full request dump is recorded including headers:

c.SetLog(os.Stdout)

Available APl functions

Full list of PayPal APl functions can be found in specification, which is divided in groups: Payments, Orders, Vault. On Client we integrate built-in functions for APl main operations.

POST /v1/oauth2/token – receiving temporary access_token

accessToken, err := c.GetAccessToken()

Application is responsible for key storage, that’s why instead of getting a new key, it is possible to install the secured one.

token := "abcdef"
                        c.SetAccessToken(token)

POST /v1/payments/payment – payment creation in PayPal

We provided two functions for payment creation.

1.Internal PayPal payment

amount := paypalsdk.Amount{
    Total:    "7.00",
    Currency: "USD",
}
redirectURI := "http://example.com/redirect-uri"
cancelURI := "http://example.com/cancel-uri"
description := "Description for this payment"
paymentResult, err := c.CreateDirectPaypalPayment(amount, redirectURI, cancelURI, description)

2.Any type payment

p := paypalsdk.Payment{
    Intent: "sale",
    Payer: &paypalsdk.Payer{
        PaymentMethod: "credit_card",
        FundingInstruments: []paypalsdk.FundingInstrument{paypalsdk.FundingInstrument{
            CreditCard: &paypalsdk.CreditCard{
                Number:      "4111111111111111",
                Type:        "visa",
                ExpireMonth: "11",
                ExpireYear:  "2020",
                CVV2:        "777",
                FirstName:   "John",
                LastName:    "Doe",
            },
        }},
    },
    Transactions: []paypalsdk.Transaction{paypalsdk.Transaction{
        Amount: &paypalsdk.Amount{
            Currency: "USD",
            Total:    "7.00",
        },
        Description: "My Payment",
    }},
    RedirectURLs: &paypalsdk.RedirectURLs{
        ReturnURL: "http://...",
        CancelURL: "http://...",
    },
}
paymentResponse, err := client.CreatePayment(p)

GET /v1/payments/payment/ID – receiving payment information

payment, err := c.GetPayment(paymentID)

GET /v1/payments/payment – list of all payments

payments, err := c.GetPayments()

GET /v1/payments/authorization/ID – receiving authorization information

authID := "2DC87612EK520411B"
auth, err := c.GetAuthorization(authID)

POST /v1/payments/authorization/ID/capture – authorization lock

capture, err := c.CaptureAuthorization(authID, &paypalsdk.Amount{Total: "7.00", Currency: "USD"}, true)

POST /v1/payments/authorization/ID/void – authorization cancxellation

auth, err := c.VoidAuthorization(authID)

POST /v1/payments/authorization/ID/reauthorize - reauthorization

auth, err := c.ReauthorizeAuthorization(authID, &paypalsdk.Amount{Total: "7.00", Currency: "USD"})

GET /v1/payments/sale/ID – receiving sale item

saleID := "36C38912MN9658832"
sale, err := c.GetSale(saleID)

POST /v1/payments/sale/ID/refund - refund for sale item. It is possible to make either full refund or partial.

// Full
refund, err := c.RefundSale(saleID, nil)
// Partial
refund, err := c.RefundSale(saleID, &paypalsdk.Amount{Total: "7.00", Currency: "USD"})

GET /v1/payments/refund/ID – receiving refund information

orderID := "O-4J082351X3132253H"
refund, err := c.GetRefund(orderID)

GET /v1/payments/orders/ID – receiving order information

order, err := c.GetOrder(orderID)

POST /v1/payments/orders/ID/authorize – order authorization

auth, err := c.AuthorizeOrder(orderID, &paypalsdk.Amount{Total: "7.00", Currency: "USD"})

POST /v1/payments/orders/ID/capture – order lock (it can be partial or full, according to sent Amount and IsFinalTransaction)

capture, err := c.CaptureOrder(orderID, &paypalsdk.Amount{Total: "7.00", Currency: "USD"}, true, nil)

POST /v1/payments/orders/ID/do-void – order cancellation

order, err := c.VoidOrder(orderID)

You can also use godoc documentation for recognizing all Client functions: https://godoc.org/github.com/logpacker/PayPal-Go-SDK

Testing and CI

We have implemented two types of tests in the project: Unit and Integration. Unit tests allow you to check operativity of internal conditions and validation. The validation sample of input parameters in NewClient function:

_, err := NewClient("", "", "")
if err == nil {
    t.Errorf("All arguments are required in NewClient()")
} else {
    fmt.Println(err.Error())
}

Integration tests work directly with test data on PayPal Sandbox, they check server responses and its conversion to go-structure.

This process is outlined in the diagram below:

Testing in PayPal sandbox diagram

The validation sample of function response CreateDirectPaypalPayment:

c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
c.GetAccessToken()

amount := Amount{
    Total:    "15.11",
    Currency: "USD",
}

p, err := c.CreateDirectPaypalPayment(amount, "http://example.com", "http://example.com", "test payment")

if err != nil || p.ID == "" {
    t.Errorf("Test paypal payment is not created")
}

We have created test account in PayPal sandbox, so we can use test ID for any type of request. For example, we can test payment information on ID PAY-5YK922393D847794YKER7MUI payment. To inform the Client that you are working with Sandbox, you need to install basic URL API (after testing change it to Live URL):

c, err := paypalsdk.NewClient("clientID", "secretID", paypalsdk.APIBaseSandBox)

Tests can be run locally by go test command, but you can’t be 100 % sure, that code in repository will be always stable. That’s why we use Continuous Integration (CI) for automatic test run at every push in repository. We use TravisCI, it can be easily integrated with GitHub repository. At the root of our project lies .travis.yml configuration:

language: go
go:
 - 1.5
install:
 - export PATH=$PATH:$HOME/gopath/bin
script:
 - go test -v

Open Source and short-term plans

All our developments you can find on GitHub, everything is published with MIT license. In plan we want to create standard library for Go and provide full API cover (+webapps, etc.)

Updated documentation can be found on project page on GitHub.

Looking forward to receiving your comments and pull-requests at logpacker/PayPal-Go-SDK.