github.com/Ingenico-ePayments/connect-sdk-go@v0.0.0-20240318153750-1f8cd329b9c9/Client_Payments_test.go (about)

     1  package connectsdk
     2  
     3  import (
     4  	"net/url"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/Ingenico-ePayments/connect-sdk-go/communicator"
    10  	"github.com/Ingenico-ePayments/connect-sdk-go/communicator/communication"
    11  	"github.com/Ingenico-ePayments/connect-sdk-go/defaultimpl"
    12  	"github.com/Ingenico-ePayments/connect-sdk-go/domain/payment"
    13  	"github.com/Ingenico-ePayments/connect-sdk-go/errors"
    14  	"github.com/Ingenico-ePayments/connect-sdk-go/logging"
    15  )
    16  
    17  func CheckSuccess(cv *TestConnection, resp payment.CreateResponse, err error) string {
    18  	if err != nil {
    19  		return "Error during request: " + err.Error()
    20  	}
    21  	if payment := resp.Payment; payment == nil || payment.ID == nil || *payment.ID != "000002000020142549460000100001" {
    22  		return "The ID of the payment is not equal to 000002000020142549460000100001"
    23  	}
    24  	if payment := resp.Payment; payment == nil || payment.Status == nil || *payment.Status != "PENDING_APPROVAL" {
    25  		return "The status of the payment is unequal to PENDING_APPROVAL"
    26  	}
    27  	return ""
    28  }
    29  
    30  func CheckRejected(cv *TestConnection, res payment.CreateResponse, err error) string {
    31  	if err == nil {
    32  		return "Error expected"
    33  	}
    34  	if dpe, ok := err.(*errors.DeclinedPaymentError); ok {
    35  		res := dpe.PaymentResult()
    36  		if res == nil {
    37  			return "PaymentResult is nil"
    38  		}
    39  		if res.Payment.ID == nil {
    40  			return "PaymentResult.ID is nil"
    41  		}
    42  		if *res.Payment.ID != "000002000020142544360000100001" {
    43  			return "PaymentResult.ID is not 000002000020142544360000100001"
    44  		}
    45  		if res.Payment.Status == nil {
    46  			return "PaymentResult.Status is nil"
    47  		}
    48  		if *res.Payment.Status != "REJECTED" {
    49  			return "PaymentResult.Status is not equal to " + "REJECTED"
    50  		}
    51  	} else {
    52  		return "Expected DeclinedPaymentError, but got different error"
    53  	}
    54  	estr := err.Error()
    55  	if !strings.Contains(estr, "payment '000002000020142544360000100001'") {
    56  		return "Error message does not contain" + "payment '000002000020142544360000100001'"
    57  	}
    58  	if !strings.Contains(estr, "status 'REJECTED'") {
    59  		return "Error message: " + estr + " does not contain " + "status 'REJECTED'"
    60  	}
    61  	if !strings.Contains(estr, rejectedJSON) {
    62  		return "Error message: " + estr + " does not contain response body"
    63  	}
    64  	return ""
    65  }
    66  
    67  func CheckInvalidRequest(cv *TestConnection, res payment.CreateResponse, err error) string {
    68  	if err == nil {
    69  		return "Error expected"
    70  	}
    71  	if _, ok := err.(*errors.ValidateError); !ok {
    72  		return "Expected ValidateError, but got different error"
    73  	}
    74  	estr := err.Error()
    75  	if !strings.Contains(estr, invalidRequestJSON) {
    76  		return "Error message: " + estr + " does not contain response body"
    77  	}
    78  	return ""
    79  }
    80  
    81  func CheckInvalidAuthorization(cv *TestConnection, res payment.CreateResponse, err error) string {
    82  	if err == nil {
    83  		return "Error expected"
    84  	}
    85  	if _, ok := err.(errors.APIError); !ok {
    86  		return "Expected APIError, but got different error"
    87  	}
    88  	estr := err.Error()
    89  	if !strings.Contains(estr, invalidAuthorizationJSON) {
    90  		return "Error message: " + estr + " does not contain response body"
    91  	}
    92  	return ""
    93  }
    94  
    95  func CheckReferenceError(cv *TestConnection, res payment.CreateResponse, err error) string {
    96  	if err == nil {
    97  		return "Error expected"
    98  	}
    99  	if _, ok := err.(*errors.ReferenceError); !ok {
   100  		return "Expected ReferenceError, but got different error"
   101  	}
   102  	estr := err.Error()
   103  	if !strings.Contains(estr, duplicateRequestJSON) {
   104  		return "Error message: " + estr + " does not contain response body"
   105  	}
   106  	return ""
   107  }
   108  
   109  func CheckIdempotenceError(cv *TestConnection, res payment.CreateResponse, err error) string {
   110  	if err == nil {
   111  		return "Error expected"
   112  	}
   113  	if ie, ok := err.(*errors.IdempotenceError); ok {
   114  		if ie.IdempotenceKey() != cv.idempotenceKey {
   115  			return "Returned wrong idempotenceKey"
   116  		}
   117  	} else {
   118  		return "Expected IdempotenceError, but got different error"
   119  	}
   120  	estr := err.Error()
   121  	if !strings.Contains(estr, duplicateRequestJSON) {
   122  		return "Error message: " + estr + " does not contain response body"
   123  	}
   124  	return ""
   125  }
   126  
   127  func CheckNotFound(cv *TestConnection, res payment.CreateResponse, err error) string {
   128  	if err == nil {
   129  		return "Error expected"
   130  	}
   131  	if nfe, ok := err.(*errors.NotFoundError); ok {
   132  		if nfe.InternalError() == nil {
   133  			return "Returned NotFoundError without internal error"
   134  		}
   135  		if _, ok := nfe.InternalError().(*errors.ResponseError); !ok {
   136  			return "NotFoundError has a different internal error than ResponseError"
   137  		}
   138  		if !strings.Contains(nfe.InternalError().Error(), notFoundHTML) {
   139  			return "Error message: " + nfe.InternalError().Error() + " does not contain response body"
   140  		}
   141  	} else {
   142  		return "Expected NotFoundError, but got different error"
   143  	}
   144  
   145  	return ""
   146  }
   147  
   148  func CheckMethodNotAllowed(cv *TestConnection, res payment.CreateResponse, err error) string {
   149  	if err == nil {
   150  		return "Error expected"
   151  	}
   152  	if nfe, ok := err.(*errors.CommunicationError); ok {
   153  		if nfe.InternalError() == nil {
   154  			return "Returned CommunicationError without internal error"
   155  		}
   156  		if _, ok := nfe.InternalError().(*errors.ResponseError); !ok {
   157  			return "CommunicationError has a different internal error than ResponseError"
   158  		}
   159  		if !strings.Contains(nfe.InternalError().Error(), methodNotAllowedHTML) {
   160  			return "Error message: " + nfe.InternalError().Error() + " does not contain response body"
   161  		}
   162  	} else {
   163  		return "Expected CommunicationError, but got different error"
   164  	}
   165  
   166  	return ""
   167  }
   168  
   169  var table = []TestConnection{
   170  	// Tests that a non-failure response will not throw an exception.
   171  	{pendingApprovalJSON, 201, nil, "", CheckSuccess},
   172  
   173  	//Tests that a failure response with a payment result will return a DeclinedPaymentError.
   174  	{rejectedJSON, 400, nil, "", CheckRejected},
   175  
   176  	// Tests that a 400 failure response without a payment result will return a ValidateError.
   177  	{invalidRequestJSON, 400, nil, "", CheckInvalidRequest},
   178  
   179  	// Tests that a 401 failure response without a payment result will return a APIError.
   180  	{invalidAuthorizationJSON, 401, nil, "", CheckInvalidAuthorization},
   181  
   182  	// Tests that a 409 failure response with a duplicate request code but without an idempotence key will return a ReferenceError
   183  	{duplicateRequestJSON, 409, nil, "", CheckReferenceError},
   184  
   185  	// Tests that a 409 failure response with a duplicate request code and an idempotence key will return a IdempotenceError.
   186  	{duplicateRequestJSON, 409, nil, "key", CheckIdempotenceError},
   187  
   188  	// Tests that a 404 response with a non-JSON response will throw a NotFoundException.
   189  	{notFoundHTML, 404, []communication.Header{newHeader("content-type", "text/html")}, "", CheckNotFound},
   190  
   191  	// Tests that a 405 response with a non-JSON response will throw a CommunicationException.
   192  	{methodNotAllowedHTML, 405, []communication.Header{newHeader("content-type", "text/html")}, "", CheckMethodNotAllowed},
   193  }
   194  
   195  func newHeader(name, value string) communication.Header {
   196  	h, _ := communication.NewHeader(name, value)
   197  	return *h
   198  }
   199  
   200  type TestConnection struct {
   201  	body           string
   202  	statusCode     int
   203  	headers        []communication.Header
   204  	idempotenceKey string
   205  	checkf         CheckResult
   206  }
   207  
   208  const pendingApprovalJSON string = `{
   209      "creationOutput": {
   210          "additionalReference": "00000200002014254946",
   211          "externalReference": "000002000020142549460000100001"
   212      },
   213      "payment": {
   214          "id": "000002000020142549460000100001",
   215          "paymentOutput": {
   216              "amountOfMoney": {
   217                  "amount": 2345,
   218                  "currencyCode": "CAD"
   219              },
   220              "references": {
   221                  "paymentReference": "0"
   222              },
   223              "paymentMethod": "card",
   224              "cardPaymentMethodSpecificOutput": {
   225                  "paymentProductId": 1,
   226                  "authorisationCode": "OK1131",
   227                  "card": {
   228                      "cardNumber": "************9176",
   229                      "expiryDate": "1220"
   230                  },
   231                  "fraudResults": {
   232                      "fraudServiceResult": "error",
   233                      "avsResult": "X",
   234                      "cvvResult": "M"
   235                  }
   236              }
   237          },
   238          "status": "PENDING_APPROVAL",
   239          "statusOutput": {
   240              "isCancellable": true,
   241              "statusCode": 600,
   242              "statusCodeChangeDateTime": "20150331120036",
   243              "isAuthorized": true
   244          }
   245      }
   246  }`
   247  
   248  const rejectedJSON = `{
   249      "errorId": "2c164323-20d3-4e9e-8578-dc562cd7506c-0000003c",
   250      "errors": [
   251          {
   252              "code": "21000020",
   253              "requestId": "2001798",
   254              "message": "VALUE **************** OF FIELD CREDITCARDNUMBER DID NOT PASS THE LUHNCHECK"
   255          }
   256      ],
   257      "paymentResult": {
   258          "creationOutput": {
   259              "additionalReference": "00000200002014254436",
   260              "externalReference": "000002000020142544360000100001"
   261          },
   262          "payment": {
   263              "id": "000002000020142544360000100001",
   264              "paymentOutput": {
   265                  "amountOfMoney": {
   266                      "amount": 2345,
   267                      "currencyCode": "CAD"
   268                  },
   269                  "references": {
   270                      "paymentReference": "0"
   271                  },
   272                  "paymentMethod": "card",
   273                  "cardPaymentMethodSpecificOutput": {
   274                      "paymentProductId": 1
   275                  }
   276              },
   277              "status": "REJECTED",
   278              "statusOutput": {
   279                  "errors": [
   280                      {
   281                          "code": "21000020",
   282                          "requestId": "2001798",
   283                          "message": "VALUE **************** OF FIELD CREDITCARDNUMBER DID NOT PASS THE LUHNCHECK"
   284                      }
   285                  ],
   286                  "isCancellable": false,
   287                  "statusCode": 100,
   288                  "statusCodeChangeDateTime": "20150330173151",
   289                  "isAuthorized": false
   290              }
   291          }
   292      }
   293  }`
   294  
   295  const invalidRequestJSON string = `{
   296      "errorId": "2c164323-20d3-4e9e-8578-dc562cd7506c-00000058",
   297      "errors": [
   298          {
   299              "code": "21000120",
   300              "requestId": "2001803",
   301              "propertyName": "cardPaymentMethodSpecificInput.card.expiryDate",
   302              "message": "paymentMethodSpecificInput.card.expiryDate (1210) IS IN THE PAST OR NOT IN CORRECT MMYY FORMAT"
   303          }
   304      ]
   305  }`
   306  
   307  const invalidAuthorizationJSON string = `{
   308      "errorId": "fbd8d914-c889-45d3-a396-9e0d9ff9db88-0000006f",
   309      "errors": [
   310          {
   311              "code": "9002",
   312              "message": "MISSING_OR_INVALID_AUTHORIZATION"
   313          }
   314      ]
   315  }`
   316  
   317  const duplicateRequestJSON string = `{
   318     "errorId" : "75b0f13a-04a5-41b3-83b8-b295ddb23439-000013c6",
   319     "errors" : [ {
   320        "code" : "1409",
   321        "message" : "DUPLICATE REQUEST IN PROGRESS",
   322        "httpStatusCode" : 409
   323     } ]
   324  }`
   325  
   326  const notFoundHTML string = "Not Found"
   327  
   328  const methodNotAllowedHTML string = "Not Allowed"
   329  
   330  type CheckResult func(cv *TestConnection, resp payment.CreateResponse, err error) string
   331  
   332  func (t *TestConnection) CloseExpiredConnections() {
   333  
   334  }
   335  func (t *TestConnection) CloseIdleConnections(num time.Duration) {
   336  
   337  }
   338  func (t *TestConnection) Close() error {
   339  	return nil
   340  }
   341  func (t *TestConnection) Get(resourceURI url.URL, requestHeaders []communication.Header, respHandler communication.ResponseHandler) (interface{}, error) {
   342  	return nil, nil
   343  }
   344  func (t *TestConnection) Delete(resourceURI url.URL, requestHeaders []communication.Header, respHandler communication.ResponseHandler) (interface{}, error) {
   345  	return nil, nil
   346  }
   347  func (t *TestConnection) Put(resourceURI url.URL, requestHeaders []communication.Header, body string, respHandler communication.ResponseHandler) (interface{}, error) {
   348  	return nil, nil
   349  }
   350  func (t *TestConnection) PutMultipart(resourceURI url.URL, requestHeaders []communication.Header, body *communication.MultipartFormDataObject, respHandler communication.ResponseHandler) (interface{}, error) {
   351  	return nil, nil
   352  }
   353  func (t *TestConnection) Post(resourceURI url.URL, requestHeaders []communication.Header, body string, respHandler communication.ResponseHandler) (interface{}, error) {
   354  	return respHandler.Handle(t.statusCode, t.headers, strings.NewReader(t.body))
   355  }
   356  func (t *TestConnection) PostMultipart(resourceURI url.URL, requestHeaders []communication.Header, body *communication.MultipartFormDataObject, respHandler communication.ResponseHandler) (interface{}, error) {
   357  	return nil, nil
   358  }
   359  func (t *TestConnection) DisableLogging() {
   360  
   361  }
   362  func (t *TestConnection) EnableLogging(l logging.CommunicatorLogger) {
   363  
   364  }
   365  
   366  func createRequestT() payment.CreateRequest {
   367  	p := payment.NewCreateRequest()
   368  	if p != nil {
   369  		return *p
   370  	}
   371  	panic("Cannot Create Request")
   372  }
   373  
   374  func TestPayment(t *testing.T) {
   375  	for _, tv := range table {
   376  		connectionMock := tv
   377  		apiEndpoint, _ := url.Parse("http://localhost")
   378  		// Error not possible
   379  
   380  		auth, _ := defaultimpl.NewDefaultAuthenticator(defaultimpl.V1HMAC, "test", "test")
   381  		// Error not possible
   382  
   383  		mp, err := communicator.NewMetaDataProviderBuilder("ingenico").Build()
   384  		if err != nil {
   385  			t.Fatalf("Cannot create MetaDataProvider: %s", err)
   386  		}
   387  		session, err := communicator.NewSession(apiEndpoint, &connectionMock, auth, mp)
   388  		if err != nil {
   389  			t.Fatal("Cannot create Session")
   390  		}
   391  		client, err := CreateClientFromSession(session)
   392  		if err != nil {
   393  			t.Fatal("Cannot create Client: ", err)
   394  		}
   395  		body := createRequestT()
   396  		cc := &CallContext{}
   397  		var resp payment.CreateResponse
   398  		if tv.idempotenceKey != "" {
   399  			cc.IdempotenceKey = tv.idempotenceKey
   400  			resp, err = client.Merchant("merchantId").Payments().Create(body, cc)
   401  		} else {
   402  			resp, err = client.Merchant("merchantId").Payments().Create(body, nil)
   403  		}
   404  		if str := tv.checkf(&tv, resp, err); str != "" {
   405  			t.Fatal(str)
   406  		}
   407  	}
   408  }