/* * SPDX-FileCopyrightText: 2023 jordi fita mas * 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) }