~ghost08/gopay-go-api

825c03076f3f13727fd6812dcdd102bce0a1979a — Vladimír Magyar 2 years ago cd0a9b8
feat: error parsing
4 files changed, 304 insertions(+), 76 deletions(-)

A README.md
M gopay.go
M gopay_test.go
M objects.go
A README.md => README.md +1 -0
@@ 0,0 1,1 @@
# GoPay's Go SDK for Payments REST API

M gopay.go => gopay.go +17 -2
@@ 4,7 4,9 @@ import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"time"
)



@@ 50,7 52,7 @@ func (c *Client) addAuthorization(req *http.Request) {
	req.Header.Set("Authorization", "Bearer "+c.token.AccessToken)
}

func (c *Client) CreatePayment(p Payment) (interface{}, error) {
func (c *Client) CreatePayment(p *Payment) (*PaymentResp, error) {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(p); err != nil {
		return nil, err


@@ 62,11 64,24 @@ func (c *Client) CreatePayment(p Payment) (interface{}, error) {
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	c.addAuthorization(req)
	data, _ := httputil.DumpRequest(req, true)
	log.Println(string(data))
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	data, _ = httputil.DumpResponse(resp, true)
	log.Println(string(data))
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("create payment response status: %s", resp.Status)
		var errorResp ErrorResp
		if err := json.NewDecoder(resp.Body).Decode(&errorResp); err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("create payment response status: %s\n%w", resp.Status, errorResp)
	}
	var paymentResp PaymentResp
	if err := json.NewDecoder(resp.Body).Decode(&paymentResp); err != nil {
		return nil, fmt.Errorf("create payment - decoding response: %w", err)
	}
	return &paymentResp, nil
}

M gopay_test.go => gopay_test.go +60 -2
@@ 8,7 8,7 @@ import (
func TestLogin(t *testing.T) {
	client, err := Login("1061399163", "stDTmVXF")
	if err != nil {
		t.Fatal()
		t.Fatal(err)
	}
	log.Println(client.token)
}


@@ 18,5 18,63 @@ func TestCreatePayment(t *testing.T) {
	if err != nil {
		t.Fatal()
	}
	client.CreatePayment()
	payment := &Payment{
		Payer: Payer{
			AllowedPaymentInstruments: []PaymentInstrument{
				PaymentInstrumentPaymentCard,
				PaymentInstrumentBankAccount,
			},
			DefaultPaymentInstrument: PaymentInstrumentPaymentCard,
			AllowedSwifts: []Swift{
				SwiftFIOBCZPP,
				SwiftBREXCZPP,
			},
			DefaultSwift: SwiftFIOBCZPP,
			Contact: Contact{
				FirstName:   "Zbyněk",
				LastName:    "Žák",
				Email:       "test@test.cz",
				PhoneNumber: "+420777456123",
				City:        "České Budějovice",
				Street:      "Planá 67",
				PostalCode:  "37301",
				CountryCode: "CZE",
			},
		},
		Target: Target{
			Type: "ACCOUNT",
			Goid: 8123456789,
		},
		Items: []Item{
			{
				Type:       TypeDISCOUNT,
				Name:       "Obuv",
				Amount:     119990,
				Count:      1,
				VatRate:    21,
				Ean:        1234567890123,
				ProductURL: "https://www.eshop.cz/boty/lodicky",
			},
		},
		Amount:           119990,
		Currency:         CurrencyCZK,
		OrderNumber:      "OBJ20200825",
		OrderDescription: "Obuv",
		Lang:             LangCS,
		Callback: Callback{
			ReturnURL:       "https://www.example.com/return",
			NotificationURL: "https://www.example.com/notify",
		},
		AdditionalParams: []AdditionalParam{
			{
				Name:  "invoicenumber",
				Value: "2015001003",
			},
		},
	}
	resp, err := client.CreatePayment(payment)
	if err != nil {
		t.Fatal(err)
	}
	log.Println(resp)
}

M objects.go => objects.go +226 -72
@@ 1,111 1,117 @@
package gopay

import (
	"fmt"
	"strings"
	"time"
)

type Payment struct {
	//Payment method settings and payer information
	Payer Payer `json:"payer"`
	Payer Payer `json:"payer,omitempty"`

	//Payee information
	Target Target `json:"target"`
	Target Target `json:"target,omitempty"`

	//Details of the payment items
	Items []Item `json:"items"`
	Items []Item `json:"items,omitempty"`

	//Payment amount in cents
	Amount int `json:"amount"`
	Amount int `json:"amount,omitempty"`

	//Payment currency
	Currency Currency `json:"currency"`
	Currency Currency `json:"currency,omitempty"`

	//Merchant’s order id, alphanumeric
	OrderNumber string `json:"order_number"`
	OrderNumber string `json:"order_number,omitempty"`

	//Order description, alphanumeric
	OrderDescription string `json:"order_description"`
	OrderDescription string `json:"order_description,omitempty"`

	//Payment gateway language
	Lang Lang `json:"lang"`
	Lang Lang `json:"lang,omitempty"`

	//Callback URL for processing of the payment result / Notification URL for processing of change of payment status
	Callback Callback `json:"callback"`
	Callback Callback `json:"callback,omitempty"`

	//Additional payment parameters
	//Max items: 4
	AdditionalParams []AdditionalParam `json:"additional_params"`
	AdditionalParams []AdditionalParam `json:"additional_params,omitempty"`

	//true if the payment should be preauthorized
	Preauthorization bool `json:"preauthorization"`
	Preauthorization bool `json:"preauthorization,omitempty"`

	//Contains object describing recurrence, if the payment should be recurrent
	Recurrence Recurrence `json:"recurrence"`
	Recurrence Recurrence `json:"recurrence,omitempty"`
}

type Payer struct {
	//Array of allowed payment methods
	Allowed_payment_instruments []PaymentInstrument `json:"allowed_payment_instruments"`
	AllowedPaymentInstruments []PaymentInstrument `json:"allowed_payment_instruments,omitempty"`

	//Preferred payment method
	DefaultPaymentInstrument PaymentInstrument `json:"default_payment_instrument"`
	DefaultPaymentInstrument PaymentInstrument `json:"default_payment_instrument,omitempty"`

	//Array of allowed bank codes
	Allowed_swifts []Swift `json:"allowed_swifts"`
	AllowedSwifts []Swift `json:"allowed_swifts,omitempty"`

	//Preferred bank if default_payment_instrument is set to BANK_ACCOUNT, set by SWIFT code
	DefaultSwift Swift `json:"default_swift"`
	DefaultSwift Swift `json:"default_swift,omitempty"`

	//Customer´s data
	Contact Contact `json:"contact"`
	Contact Contact `json:"contact,omitempty"`

	//Bank account´s information
	BankAccount BankAccount `json:"bank_account"`
	BankAccount BankAccount `json:"bank_account,omitempty"`

	//Payment card´s information
	PaymentCard PaymentCard `json:"payment_card"`
	PaymentCard PaymentCard `json:"payment_card,omitempty"`

	//PIN for identification payment purposes
	VerifyPin string `json:"verify_pin"`
	VerifyPin string `json:"verify_pin,omitempty"`

	//Token for identification payment purposes
	AllowedCardRoken string `json:"allowed_card_token"`
	AllowedCardRoken string `json:"allowed_card_token,omitempty"`

	//Email of used GoPay wallet (only for GOPAY payment method)
	Email string `json:"email"`
	Email string `json:"email,omitempty"`
}

type Target struct {
	//always ACCOUNT
	Type string `json:"type"`
	Type string `json:"type,omitempty"`

	//Unique identifier of an e-shop in the payment gateway system
	//Example: 8123456789
	Goid int `json:"goid"`
	Goid int `json:"goid,omitempty"`
}

type Item struct {
	//Type of row, for registration of sales
	Type Type `json:"type"`
	Type Type `json:"type,omitempty"`

	//Product name
	//Max length: 256
	Name string `json:"name"`
	Name string `json:"name,omitempty"`

	//Total price of items in cents
	Amount int `json:"amount"`
	Amount int `json:"amount,omitempty"`

	//Number of items
	//Min: 1
	Count int `json:"count"`
	Count int `json:"count,omitempty"`

	//VAT rate
	//Min: 0
	//Max: 100
	VatRate int `json:"vat_rate"`
	VatRate int `json:"vat_rate,omitempty"`

	//EAN code of the product
	Ean int `json:"ean"`
	Ean int `json:"ean,omitempty"`

	//URL address of the product
	//Max length: 512
	ProductURL string `json:"product_url"`
	ProductURL string `json:"product_url,omitempty"`
}

type Currency string


@@ 147,7 153,7 @@ type Callback struct {

	//URL address for sending asynchronous notification in the case of changes in the payment status (with protocol)
	//Max length: 512
	NotificationURL string `json:"notification_url"`
	NotificationURL string `json:"notification_url,omitempty"`
}

type AdditionalParam struct {


@@ 163,19 169,19 @@ type AdditionalParam struct {
type Recurrence struct {
	//Time period of recurring
	//Example: MONTH
	RecurrenceCycle RecurrenceCycle `json:"recurrence_cycle"`
	RecurrenceCycle RecurrenceCycle `json:"recurrence_cycle,omitempty"`

	//Recurring period of recurring payment
	//Example: 12
	RecurrencePeriod int `json:"recurrence_period"`
	RecurrencePeriod int `json:"recurrence_period,omitempty"`

	//The period of validity recurring payment
	//Example: 2025-12-31
	RecurrenceDateTo string `json:"recurrence_date_to"`
	RecurrenceDateTo string `json:"recurrence_date_to,omitempty"`

	//Describes state of recurring payment
	//Example: STARTED
	RecurrenceState RecurrenceState `json:"recurrence_state"`
	RecurrenceState RecurrenceState `json:"recurrence_state,omitempty"`
}

type PaymentInstrument string


@@ 254,81 260,81 @@ const (
type Contact struct {
	//First name
	//Max length: 256
	FirstName string `json:"first_name"`
	FirstName string `json:"first_name,omitempty"`

	//Last name
	//Max length: 256
	LastName string `json:"last_name"`
	LastName string `json:"last_name,omitempty"`

	//Valid e-mail
	//Max length: 128
	Email string `json:"email"`
	Email string `json:"email,omitempty"`

	//Phone number with country code
	//Max length: 128
	PhoneNumber string `json:"phone_number"`
	PhoneNumber string `json:"phone_number,omitempty"`

	//City
	//Max length: 128
	City string `json:"city"`
	City string `json:"city,omitempty"`

	//Street
	//Max length: 128
	Street string `json:"street"`
	Street string `json:"street,omitempty"`

	//Postal code
	//Max length: 16
	PostalCode string `json:"postal_code"`
	PostalCode string `json:"postal_code,omitempty"`

	//Country code ISO 3166-1 alpha-3
	CountryCode string `json:"country_code"`
	CountryCode string `json:"country_code,omitempty"`
}

type BankAccount struct {
	//International bank account number
	IBAN string `json:"iban"`
	IBAN string `json:"iban,omitempty"`

	//Business identification code (SWIFT)
	BIC string `json:"bic"`
	BIC string `json:"bic,omitempty"`

	//Bank account prefix
	Prefix string `json:"prefix"`
	Prefix string `json:"prefix,omitempty"`

	//Bank account number
	AccountNumber string `json:"account_number"`
	AccountNumber string `json:"account_number,omitempty"`

	//Bank account code
	BankCode string `json:"bank_code"`
	BankCode string `json:"bank_code,omitempty"`

	//Bank account holder name
	AccountName string `json:"account_name"`
	AccountName string `json:"account_name,omitempty"`

	//Account token of a saved account for PSD2 payments
	AccountToken string `json:"account_token"`
	AccountToken string `json:"account_token,omitempty"`
}

type PaymentCard struct {
	//Masked payment card´s number
	CardNumber string `json:"card_number"`
	CardNumber string `json:"card_number,omitempty"`

	//Expiration date
	CardExpiration string `json:"card_expiration"`
	CardExpiration string `json:"card_expiration,omitempty"`

	//Payment card´s type
	CardBrand string `json:"card_brand"`
	CardBrand string `json:"card_brand,omitempty"`

	//Country code of issuing bank
	CardIssuerCountry string `json:"card_issuer_country"`
	CardIssuerCountry string `json:"card_issuer_country,omitempty"`

	//Issuing bank
	CardIssuerBank string `json:"card_issuer_bank"`
	CardIssuerBank string `json:"card_issuer_bank,omitempty"`

	//Token for identification payment purposes
	//Example: 6RuTM69kX6UUGZ6p9hyMPrTUVXmMDdkC4BNnQvQcb
	CardToken string `json:"card_token"`
	CardToken string `json:"card_token,omitempty"`

	//3D Secure authorization’s result for identification payment purposes
	Result3DS string `json:"3ds_result"`
	Result3DS string `json:"3ds_result,omitempty"`
}

type Type string


@@ 358,41 364,41 @@ const (

type PaymentResp struct {
	//Payment ID
	Id int `json:"id"`
	Id int `json:"id,omitempty"`

	//Order ID
	OrderNumber string `json:"order_number"`
	OrderNumber string `json:"order_number,omitempty"`

	//Payment status
	State State `json:"state"`
	State State `json:"state,omitempty"`

	//Amount in cents
	Amount int `json:"amount"`
	Amount int `json:"amount,omitempty"`

	//Payment currency
	Currency Currency `json:"currency"`
	Currency Currency `json:"currency,omitempty"`

	//Information about the payer and payment methods
	Payer Payer `json:"payer"`
	Payer Payer `json:"payer,omitempty"`

	//Payee information
	Target Target `json:"target"`
	Target Target `json:"target,omitempty"`

	//Additional parameters
	AdditionalParams []AdditionalParam `json:"additional_params"`
	AdditionalParams []AdditionalParam `json:"additional_params,omitempty"`

	//Payment gateway language
	Lang Lang `json:"lang"`
	Lang Lang `json:"lang,omitempty"`

	//Descibes recurrence if the payment is recurrent
	Recurrence Recurrence `json:"recurrence"`
	Recurrence Recurrence `json:"recurrence,omitempty"`

	//Describes preauthorization if the payment is preauthorized
	Preauthorization Preauthorization `json:"preauthorization"`
	Preauthorization Preauthorization `json:"preauthorization,omitempty"`

	//URL for initiation of the payment gate
	//Example: https://gw.sandbox.gopay.com/gw/v3/bCcvmwTKK5hrJx2aGG8ZnFyBJhAvF
	GwURL string `json:"gw_url"`
	GwURL string `json:"gw_url,omitempty"`
}

type State string


@@ 410,10 416,10 @@ const (

type Preauthorization struct {
	//Whether the pre-authorization was established
	Requested bool `json:"requested"`
	Requested bool `json:"requested,omitempty"`

	//Payment pre-authorization status
	State PreauthorizationState `json:"state"`
	State PreauthorizationState `json:"state,omitempty"`
}

type PreauthorizationState string


@@ 424,3 430,151 @@ const (
	PreauthorizationStateCaptured   PreauthorizationState = "CAPTURED"   //Pre-authorion captured
	PreauthorizationStateCanceled   PreauthorizationState = "CANCELED"   //Pre-authorization canceled
)

type ErrorResp struct {
	DateIssued time.Time
	Errors     []Error
}

func (er ErrorResp) Error() string {
	errors := make([]string, len(er.Errors))
	for i := 1; i < len(er.Errors); i++ {
		errors[i] = er.Errors[i].Error()
	}
	return strings.Join(errors, ", ")
}

type Error struct {
	Scope       ErrorScope //Error range
	Field       string     //Which parameter is affected by the error, unless it is a global error
	Message     string     //Localized message. Localization based on Accept-Language in the header. It is set to en-US by default.
	Description string     //Technical description of the error
	ErrorCode   ErrorCode  //Numeric designation of the error type
	ErrorName   string     //Code designation of the error type
}

func (e Error) Error() string {
	return fmt.Sprintf(
		"Scope: %s\nField: %s\nMessage: %s\nDescription: %s\nErrorCode: %s\nErrorName: %s",
		e.Scope,
		e.Field,
		e.Message,
		e.Description,
		e.ErrorCode,
		e.ErrorName,
	)
}

type ErrorScope string

const (
	ErrorScopeF ErrorScope = "F" //The error concerns a specific parameter
	ErrorScopeG ErrorScope = "G" //Global error
)

type ErrorCode int

const (
	ErrorCode100 ErrorCode = 100 //System Error
	ErrorCode110 ErrorCode = 110 //Compulsory
	ErrorCode111 ErrorCode = 111 //Wrong format
	ErrorCode112 ErrorCode = 112 //Already exists
	ErrorCode113 ErrorCode = 113 //Cannot be changed
	ErrorCode114 ErrorCode = 114 //Cannot delete
	ErrorCode115 ErrorCode = 115 //Ambiguous
	ErrorCode116 ErrorCode = 116 //Invalid request
	ErrorCode200 ErrorCode = 200 //Unauthorized access
	ErrorCode201 ErrorCode = 201 //The method of assigning rights is not supported
	ErrorCode202 ErrorCode = 202 //Wrong access data
	ErrorCode203 ErrorCode = 203 //PIN access has been disabled
	ErrorCode301 ErrorCode = 301 //Unable to create payment
	ErrorCode302 ErrorCode = 302 //Payment cannot be made
	ErrorCode303 ErrorCode = 303 //Payment in wrong condition
	ErrorCode304 ErrorCode = 304 //Payment not found
	ErrorCode305 ErrorCode = 305 //E-shop has been deactivated
	ErrorCode321 ErrorCode = 321 //The payee cannot accept the payment
	ErrorCode330 ErrorCode = 330 //Payment cannot be refunded
	ErrorCode331 ErrorCode = 331 //Payment cannot be refunded
	ErrorCode332 ErrorCode = 332 //Wrong amount
	ErrorCode333 ErrorCode = 333 //Lack of money in the account
	ErrorCode340 ErrorCode = 340 //Recurring payment failed
	ErrorCode341 ErrorCode = 341 //Recurring payment is not supported
	ErrorCode342 ErrorCode = 342 //Payment recurrence stopped
	ErrorCode343 ErrorCode = 343 //Time limit for recurring payments exceeded
	ErrorCode350 ErrorCode = 350 //Payment hold failed
	ErrorCode351 ErrorCode = 351 //Payment has already been canceled
	ErrorCode352 ErrorCode = 352 //Revocation revocation failed
	ErrorCode353 ErrorCode = 353 //Pre-authorization canceled
	ErrorCode360 ErrorCode = 360 //The sum of the amounts of the order items does not match the payment amount
	ErrorCode394 ErrorCode = 394 //Account not found
)

func (e ErrorCode) String() string {
	switch e {
	case ErrorCode100:
		return "System Error"
	case ErrorCode110:
		return "Compulsory"
	case ErrorCode111:
		return "Wrong format"
	case ErrorCode112:
		return "Already exists"
	case ErrorCode113:
		return "Cannot be changed"
	case ErrorCode114:
		return "Cannot delete"
	case ErrorCode115:
		return "Ambiguous"
	case ErrorCode116:
		return "Invalid request"
	case ErrorCode200:
		return "Unauthorized access"
	case ErrorCode201:
		return "The method of assigning rights is not supported"
	case ErrorCode202:
		return "Wrong access data"
	case ErrorCode203:
		return "PIN access has been disabled"
	case ErrorCode301:
		return "Unable to create payment"
	case ErrorCode302:
		return "Payment cannot be made"
	case ErrorCode303:
		return "Payment in wrong condition"
	case ErrorCode304:
		return "Payment not found"
	case ErrorCode305:
		return "E-shop has been deactivated"
	case ErrorCode321:
		return "The payee cannot accept the payment"
	case ErrorCode330:
		return "Payment cannot be refunded"
	case ErrorCode331:
		return "Payment cannot be refunded"
	case ErrorCode332:
		return "Wrong amount"
	case ErrorCode333:
		return "Lack of money in the account"
	case ErrorCode340:
		return "Recurring payment failed"
	case ErrorCode341:
		return "Recurring payment is not supported"
	case ErrorCode342:
		return "Payment recurrence stopped"
	case ErrorCode343:
		return "Time limit for recurring payments exceeded"
	case ErrorCode350:
		return "Payment hold failed"
	case ErrorCode351:
		return "Payment has already been canceled"
	case ErrorCode352:
		return "Revocation revocation failed"
	case ErrorCode353:
		return "Pre-authorization canceled"
	case ErrorCode360:
		return "The sum of the amounts of the order items does not match the payment amount"
	case ErrorCode394:
		return "Account not found"
	}
	return fmt.Sprintf("%d", e)
}