Go-client for PayPal API
LogPacker is a log collection, analysis, and transfer service. It has a Go backend. Check out this neat use case in integrating the Go-client with the PayPal API!
Join the DZone community and get the full member experience.
Join For FreeHey! We have developed the LogPacker 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 API. 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 worthy, 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 the sandbox, where we tested all kinds of API requests.
The 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 a certain period of time. After that, all requests to PayPal should be followed by this access_token in the request message header (-u ":").
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 API. E.g., for creating a payment, you need the following command:
paymentResponse, err := client.CreatePayment(p)
We work on providing and describing all API 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 to the log file, the full request dump is recorded including headers:
c.SetLogFile("/tpm/paypal-debug.log")
Available APl functions
A full list of PayPal API functions can be found in the specification, which is divided into groups: Payments, Orders, Vault. On Client, we integrate built-in functions for API main operations.
POST /v1/oauth2/token – receiving temporary access_token
accessToken, err := c.GetAccessToken()
The 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:
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 a 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 the repository will be always stable. That’s why we use Continuous Integration (CI) for an automatic test run at every push in the 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 the project page on GitHub.
Looking forward to receiving your comments and pull-requests at logpacker/PayPal-Go-SDK.
Opinions expressed by DZone contributors are their own.
Comments