Skip to content

Instantly share code, notes, and snippets.

@mndrix
Last active October 28, 2016 18:59
Show Gist options
  • Save mndrix/acb5bbdb928cf7b26bde229989ed2f84 to your computer and use it in GitHub Desktop.
Save mndrix/acb5bbdb928cf7b26bde229989ed2f84 to your computer and use it in GitHub Desktop.
Abandoned attempt at a nice Go package for PayPal's NVP API. That API is too much of an abomination to proceed
package nvelop // import "github.com/mndrix/nvelop"
import (
"net/http"
"net/url"
)
type Client struct {
// TODO
HttpClient *http.Client
}
func (client *Client) Request(method string, version int, endpoint *url.URL) error {
// TODO method to perform a generic request
// TODO should take an authenticator to add auth details to the request
// TODO should
return nil
}
package nvelop // import "github.com/mndrix/nvelop"
import "testing"
func TestFoo(t *testing.T) {
// TODO simple test for GetPalDetails call
}
package nvelop // import "github.com/mndrix/nvelop"
type Endpoint string
type Service int
const (
Merchant Service = iota
Payflow Service = iota
AdaptiveAccounts Service = iota
AdaptivePayments Service = iota
Invoicing Service = iota
Permissions Service = iota
)
// TODO represent the information at https://developer.paypal.com/docs/classic/api/endpoints/
// TODO as Go data types, etc.
package nvelop // import "github.com/mndrix/nvelop"
import "log"
type GetBalanceRequest struct {
ReturnAllCurrencies bool `nvelop:"RETURNALLCURRENCIES,v51"`
}
func (*GetBalanceRequest) Service() Service { return Merchant }
func (*GetBalanceRequest) Method() string { return "GetBalance" }
// sample response text:
// L_AMT0=0%2e00&L_CURRENCYCODE0=USD&TIMESTAMP=2016%2d10%2d28T15%3a48%3a19Z&CORRELATIONID=665dcabc3a98f&ACK=Success&VERSION=204%2e0&BUILD=24616352
type GetBalanceResponse struct {
Response
// TODO probably should be `Amounts []GetBalanceAmount` with
// TODO type GetBalanceAmount struct { Amount L_AMT; Currency L_CURRENCYCODE }
// TODO the trailing 0 is the index into Amounts but name is index into GetBalanceAmount struct
// TODO position of the "trailing 0" needs to be configurable since Express Checkout puts it in the middle
// TODO needs to support something like "L_FOO_{n}_BAR{m}" for nested arrays
Amounts []int `nvelop:"L_AMT,money"`
Currencies []string `nvelop:"L_CURRENCYCODE"`
}
// BalanceFor returns the balance (in pennies) for the given currency. Returns
// false if balance information for the given currency is not available.
func (self *GetBalanceResponse) BalanceFor(currency string) (int, bool) {
if len(self.Amounts) != len(self.Currencies) {
log.Panicf("GetBalanceResponse slices have different lengths: %d vs %d", len(self.Amounts), len(self.Currencies))
}
for i, c := range self.Currencies {
if c == currency {
return self.Amounts[i], true
}
}
return 0, false
}
package nvelop // import "github.com/mndrix/nvelop"
type GetPalDetailsRequest struct {
}
func (*GetPalDetailsRequest) Service() Service { return Merchant }
func (*GetPalDetailsRequest) Method() string { return "GetPalDetails" }
// sample response text:
// PAL=WH8M3P3T9FDVL&LOCALE=en_US&TIMESTAMP=2016%2d10%2d28T15%3a50%3a48Z&CORRELATIONID=3db311449346f&ACK=Success&VERSION=204%2e0&BUILD=24616352
type GetPalDetailsResponse struct {
Response
Pal string `nvelop:"PAL"`
Locale string `nvelop:"LOCALE"`
}
package nvelop // import "github.com/mndrix/nvelop"
import "time"
type Response struct {
Ack string `nvelop:"ACK"`
CorrelationId string `nvelop:"CORRELATIONID"`
Time time.Time `nvelop:"TIMESTAMP"`
Version string `nvelop:"VERSION"`
Build int `nvelop:"BUILD"`
Errors []Error
}
type Error struct {
Code int `nvelop:"L_ERRORCODE"`
ShortMessage string `nvelop:"L_SHORTMESSAGE"`
LongMessage string `nvelop:"L_LONGMESSAGE"`
Severity string `nvelop:"L_SEVERITYCODE"`
ParamId string `nvelop:"L_ERRORPARAMID"`
ParamValue string `nvelop:"L_ERRORPARAMVALUE"`
}
// See
// https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout_API_Operation_NVP/
// for full API documentation.
package nvelop // import "github.com/mndrix/nvelop"
import "net/url"
type Channel string
type CheckoutFlow string
type Page string
type PaymentReason string
type PaymentMethod string
type PaymentAction string
const (
Sole CheckoutFlow = "Sole"
Mark CheckoutFlow = "Mark"
Billing Page = "Billing"
Login Page = "Login"
Merchant Channel = "Merchant"
EbayItem Channel = "eBayItem"
None PaymentReason = "None"
Refund PaymentReason = "Refund"
InstantOnly PaymentMethod = "InstantPaymentOnly"
Sale PaymentAction = "Sale"
Authorization PaymentAction = "Authorization"
Order PaymentAction = "Order"
)
type SetExpressCheckoutRequest struct {
Token string `nvelop:"TOKEN"`
PaymentRequests []*PaymentRequest `nvelop:"PAYMENTREQUEST_"`
MaximumAmount int `nvelop:"MAXAMT,money"`
ReturnUrl *url.URL `nvelop:"RETURNURL"`
CancelUrl *url.URL `nvelop:"CANCELURL"`
CallbackUrl *url.URL `nvelop:"CALLBACK"`
CallbackTimeout int `nvelop:"CALLBACKTIMEOUT"`
CallbackVersion string `nvelop:"CALLBACKVERSION"`
RequireConfirmedAddress bool `nvelop:"REQCONFIRMSHIPPING"` // TODO need way to omit field entirely
NoShipping int `nvelop:"NOSHIPPING"`
AllowNote bool `nvelop:"ALLOWNOTE"`
AddressOverride bool `nvelop:"ADDROVERRIDE"`
Locale string `nvelop:"LOCALECODE"`
PageStyle string `nvelop:"PAGESTYLE"`
HeaderImage *url.URL `nvelop:"HDRIMG"`
PayflowColor string `nvelop:"PAYFLOWCOLOR"`
CartBorderColor string `nvelop:"CARTBORDERCOLOR"`
LogoImage *url.URL `nvelop:"LOGOIMG"`
BuyerEmail string `nvelop:"EMAIL"`
SolutionType CheckoutFlow `nvelop:"SOLUTIONTYPE"`
LandingPage Page `nvelop:"LANDINGPAGE"`
ChannelType Channel `nvelop:"CHANNELTYPE"`
GiropaySuccessUrl *url.URL `nvelop:"GIROPAYSUCCESSURL"`
GiropayCancelUrl *url.URL `nvelop:"GIROPAYCANCELURL"`
BankTransferUrl *url.URL `nvelop:"BANKTXNPENDINGURL"`
BrandName string `nvelop:"BRANDNAME"`
CustomerServiceNumber string `nvelop:"CUSTOMERSERVICENUMBER"`
Note string `nvelop:"NOTETOBUYER"`
}
func (*SetExpressCheckoutRequest) Service() Service { return Merchant }
func (*SetExpressCheckoutRequest) Method() string { return "SetExpressCheckout" }
type SetExpressCheckoutResponse struct {
Response
}
type PaymentRequest struct {
Id string `nvelop:"_PAYMENTREQUESTID"`
Amount int `nvelop:"_AMT,money"`
Currency string `nvelop;"_CURRENCYCODE"`
Reason PaymentReason `nvelop:"_PAYMENTREASON"`
ShipTo Address `nvelop:"_SHIPTO"`
ItemAmount int `nvelop:"_ITEMAMT,money"`
ShippingAmount int `nvelop:"_SHIPPINGAMT,money"`
InsuranceAmount int `nvelop:"_INSURANCEAMT,money"`
ShippingDiscount int `nvelop:"_SHIPDISCAMT,money"` // negative number
InsuranceOffered bool `nvelop:"_INSURANCEOPTIONOFFERED"` // TODO boolean renders as "true" or "false" for this field
HandlingAmount int `nvelop:"_HANDLINGAMT,money"`
TaxAmount int `nvelop:"_TAXAMT,money"`
Description string `nvelop:"_DESC"`
Custom string `nvelop:"_CUSTOM"`
InvoiceNumber string `nvelop:"_INVNUM"`
NotifyUrl *url.URL `nvelop:"_NOTIFYURL"`
MultiShipping bool `nvelop:"_MULTISHIPPING"`
Note string `nvelop:"_NOTETEXT"`
// error in the docs? field is only part of DoExpressCheckout response
//TransactionId string `nvelop:"_TRANSACTIONID"`
AllowedPayment PaymentMethod `nvelop:"_ALLOWEDPAYMENTMETHOD"`
PaymentAction PaymentAction `nvelop:"_PAYMENTACTION"`
Bucket int `nvelop:"_BUCKETCATEGORYTYPE"`
LocationType int `nvelop:"_LOCATION_TYPE,v115"`
LocationId string `nvelop:"_LOCATION_ID,v115"`
// TODO this field needs L_PAYMENTREQUEST_n prefix instead of PAYMENTREQUEST_n
Items []PaymentRequestItem
}
type PaymentRequestItem struct {
Name string
Description string
Amount int // money
Number string
Quantity int
TaxAmount int // money
Weight int
Length int
Width int
Height int
WeightUnit string
LengthUnit string
WidthUnit string
HeighUnit string
}
type Address struct {
Name string `nvelop:"NAME"`
Street string `nvelop:"STREET"`
Street2 string `nvelop:"STREET2"`
City string `nvelop:"CITY"`
State string `nvelop:"STATE"`
Zip string `nvelop:"ZIP"`
Country string `nvelop:"COUNTRYCODE"`
Phone string `nvelop:"PHONENUM"`
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment