camper/pkg/redsys/client.go

164 lines
4.5 KiB
Go

/*
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
* SPDX-License-Identifier: AGPL-3.0-only
*/
package redsys
import (
"context"
"fmt"
"github.com/jackc/pgtype"
"golang.org/x/text/language"
"time"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
)
type TransactionType int
type ResponseCode int
const (
TransactionTypeCharge TransactionType = 0
TransactionTypeRefund TransactionType = 3
TransactionTypePreauth TransactionType = 1
TransactionTypePreauthConfirm TransactionType = 2
TransactionTypePreauthVoid TransactionType = 9
TransactionTypeSplitAuth TransactionType = 7
TransactionTypeSplitConfirm TransactionType = 8
ResponsePaymentAuthorized ResponseCode = 99
ResponseRefundAuthorized ResponseCode = 900
ResponseCancelAuthorized ResponseCode = 400
ResponseCardExpired ResponseCode = 101
ResponseCardException ResponseCode = 102
ResponseTooManyPINAttempts ResponseCode = 106
ResponseCardNotEffective ResponseCode = 125
ResponseIncorrectSecureCode ResponseCode = 129
ResponseDenied ResponseCode = 172
)
type Request struct {
TransactionType TransactionType
Amount string
OrderNumber string
Product string
CardHolder string
SuccessURL string
FailureURL string
NotificationURL string
ConsumerLanguage language.Tag
}
func (req Request) Sign(ctx context.Context, conn *database.Conn, company *auth.Company) (*SignedRequest, error) {
row := conn.QueryRow(ctx, "select redsys_sign_request($1, $2)", company.ID, req)
signed := &SignedRequest{}
if err := row.Scan(&signed); err != nil {
return nil, err
}
return signed, nil
}
func (req Request) EncodeText(ci *pgtype.ConnInfo, dst []byte) ([]byte, error) {
typeName := database.RedsysRequestTypeName
dt, ok := ci.DataTypeForName(typeName)
if !ok {
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
}
values := []interface{}{
req.TransactionType,
req.Amount,
req.OrderNumber,
req.Product,
req.CardHolder,
req.SuccessURL,
req.FailureURL,
req.NotificationURL,
req.ConsumerLanguage,
}
ct := pgtype.NewValue(dt.Value).(*pgtype.CompositeType)
if err := ct.Set(values); err != nil {
return nil, err
}
return ct.EncodeText(ci, dst)
}
type SignedRequest struct {
MerchantParameters string
Signature string
SignatureVersion string
}
func (dst *SignedRequest) DecodeText(ci *pgtype.ConnInfo, src []byte) error {
typeName := database.RedsysSignedRequestTypeName
dt, ok := ci.DataTypeForName(typeName)
if !ok {
return fmt.Errorf("unable to find oid for type name %v", typeName)
}
ct := pgtype.NewValue(dt.Value).(*pgtype.CompositeType)
if err := ct.DecodeText(ci, src); err != nil {
return err
}
return ct.AssignTo(dst)
}
type SignedResponse struct {
MerchantParameters string
Signature string
SignatureVersion string
}
func (signed SignedResponse) Decode(ctx context.Context, conn *database.Conn, company *auth.Company) (*Response, error) {
row := conn.QueryRow(ctx, "select redsys_decode_response($1, $2, $3, $4)", company.ID, signed.MerchantParameters, signed.Signature, signed.SignatureVersion)
response := &Response{}
if err := row.Scan(&response); err != nil {
return nil, err
}
return response, nil
}
type Response struct {
MerchantCode string
TerminalNumber int
ResponseCode ResponseCode
DateTime time.Time
SecurePayment bool
TransactionType TransactionType
Amount string
CurrencyCode string
OrderNumber string
AuthorizationCode string
ErrorCode string
}
func (response *Response) Process(ctx context.Context, conn *database.Conn, paymentSlug string) (string, error) {
return conn.GetText(ctx, "select process_payment_response($1, $2)", paymentSlug, response)
}
func (response *Response) EncodeBinary(ci *pgtype.ConnInfo, dst []byte) ([]byte, error) {
typeName := database.RedsysResponseTypeName
dt, ok := ci.DataTypeForName(typeName)
if !ok {
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
}
values := []interface{}{
response.MerchantCode,
response.TerminalNumber,
response.ResponseCode,
response.DateTime,
response.SecurePayment,
response.TransactionType,
response.Amount,
response.CurrencyCode,
response.OrderNumber,
response.AuthorizationCode,
response.ErrorCode,
}
ct := pgtype.NewValue(dt.Value).(*pgtype.CompositeType)
if err := ct.Set(values); err != nil {
return nil, err
}
return ct.EncodeBinary(ci, dst)
}