From cca5307bc7be7ce5b7e2647c63894ffb6b47bc60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=BDubom=C3=ADr=20Samotn=C3=BD?= Date: Fri, 4 Jul 2025 14:25:50 +0200 Subject: [PATCH 1/3] feat(loan): add loanPrecalculation operation --- src/index.ts | 23 +++ src/paths.ts | 224 +++++++++++++++-------- tatrapayplus_api_sandbox.json | 322 +++++++++++++++++++++++----------- tests/live.test.ts | 94 +++++++++- 4 files changed, 487 insertions(+), 176 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9fa3c8d..48451e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -347,4 +347,27 @@ export class TBPlusSDK { return base64Encoded.replace(/(.{64})/g, "$1\n"); } + + public async precalculateLoan( + body: paths["/v1/payments/loans/precalculation"]["put"]["requestBody"]["content"]["application/json"], + clientIp: string, + ) { + const response = await this.apiClient.PUT("/v1/payments/loans/precalculation", { + params: { + header: { + ...this.getDefaultHeaders(), + "IP-Address": clientIp, + }, + }, + body: body, + }); + if (!response.data) { + return response; + } + + return { + ...response, + data: response.data, + }; + } } diff --git a/src/paths.ts b/src/paths.ts index 00088b5..0a38d3a 100644 --- a/src/paths.ts +++ b/src/paths.ts @@ -88,7 +88,7 @@ export interface paths { patch: operations["updatePaymentIntent"]; trace?: never; }; - "/v1/payments-direct": { + "/v1/payments/loans/precalculation": { parameters: { query?: never; header?: never; @@ -96,19 +96,19 @@ export interface paths { cookie?: never; }; get?: never; - put?: never; /** - * Create direct transaction request - * @description After obtaining the token from the Google Pay API or Apple Pay API, you need to send the token along with other payment details to the TatraPayPlus API. + * Loan precalculation + * @description Loan precalculation */ - post: operations["createDirectTransactionRequest"]; + put: operations["loanPrecalculation"]; + post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/v1/appearances": { + "/v1/payments-direct": { parameters: { query?: never; header?: never; @@ -118,17 +118,17 @@ export interface paths { get?: never; put?: never; /** - * Set appearance parameters for TatraPayPlus - * @description Set appearance parameters for TatraPayPlus + * Create direct transaction request + * @description After obtaining the token from the Google Pay API or Apple Pay API, you need to send the token along with other payment details to the TatraPayPlus API. */ - post: operations["setAppearance"]; + post: operations["createDirectTransactionRequest"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/v1/appearances/logo": { + "/v1/appearances": { parameters: { query?: never; header?: never; @@ -138,20 +138,17 @@ export interface paths { get?: never; put?: never; /** - * Set logo for TatraPayPlus - * @description Logo appearance request body. Logo must follow these rules: - * MaxLength: 256px - * MaxHeight: 64px - * + * Set appearance parameters for TatraPayPlus + * @description Set appearance parameters for TatraPayPlus */ - post: operations["setLogo"]; + post: operations["setAppearance"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/auth/oauth/v2/token": { + "/v1/appearances/logo": { parameters: { query?: never; header?: never; @@ -161,10 +158,13 @@ export interface paths { get?: never; put?: never; /** - * Obtain OAuth2 Access Token - * @description Retrieves an access token using client credentials. + * Set logo for TatraPayPlus + * @description Logo appearance request body. Logo must follow these rules: + * MaxLength: 256px + * MaxHeight: 64px + * */ - post: operations["getAccessToken"]; + post: operations["setLogo"]; delete?: never; options?: never; head?: never; @@ -175,6 +175,64 @@ export interface paths { export type webhooks = Record; export interface components { schemas: { + basicCalculationResponseItem: { + loanInterestRate: components["schemas"]["loanInterestRate"]; + installmentAmount: components["schemas"]["installmentAmount"]; + loanDuration: components["schemas"]["loanDuration"]; + preference: components["schemas"]["preference"]; + mainPreference: components["schemas"]["mainPreference"]; + capacityValidity: components["schemas"]["capacityValidity"]; + rpmn: components["schemas"]["rpmn"]; + totalAmount: components["schemas"]["totalAmount"]; + loanFee: components["schemas"]["loanFee"]; + }; + /** + * Format: double + * @description Loan interest rate + */ + loanInterestRate: number; + /** + * Format: double + * @description Installment amount + */ + installmentAmount: number; + /** @description Loan duration */ + loanDuration: number; + /** @description Preferred maturity of loan offer (max 3) */ + preference: boolean; + /** @description Main preferred maturity of loan offer (max 1) */ + mainPreference: boolean; + /** @description Loan offer is valid with respect to entered capacity */ + capacityValidity: boolean; + /** + * Format: double + * @description Calculated RPMN + */ + rpmn: number; + /** + * Format: double + * @description Total amount of the order including all fees, insurance, shipping,... + * @example 156.95 + */ + totalAmount: number; + /** + * Format: double + * @description Loan amount in EUR + * @example 156.95 + */ + loanAmount: number; + /** Format: double */ + loanFee: number; + basicCalculationResponse: components["schemas"]["basicCalculationResponseItem"][]; + basicCalculationRequest: { + /** + * @description Only if isPrecalculationAllowed = true + * @enum {string} + */ + paymentMethod?: "PAY_LATER"; + loanAmount: components["schemas"]["loanAmount"]; + capacityInfo?: components["schemas"]["capacityInfo"]; + }; /** @description * **TatraPayPlus payment update response. ** * @@ -270,7 +328,7 @@ export interface components { /** @description If true - pre-authorization transaction */ isPreAuthorization?: boolean; tdsData: components["schemas"]["directTransactionTDSData"]; - ipspData?: components["schemas"]["directTransactionIPSPData"]; + ipspData?: components["schemas"]["transactionIPSPData"]; token: components["schemas"]["token"]; }; /** @description Body for payment initiation */ @@ -322,7 +380,11 @@ export interface components { | "CB_NOT_FOUND" | "CB_TOO_OLD" | "CB_ERROR" - | "NO_AVAIL_PAY_METH"; + | "NO_AVAIL_PAY_METH" + | "LOAN_AMNT_LOW" + | "LOAN_AMNT_HIGH" + | "NEGATIVE_VALUE_NOT_ALLOWED" + | "INSUFFICIENT_CAPACITY"; errorDescription?: string; /** @description Reason codes of declined methods */ availablePaymentMethods?: components["schemas"]["availablePaymentMethod"][]; @@ -588,6 +650,8 @@ export interface components { supportedCurrency?: components["schemas"]["supportedCurrency"]; supportedCountry?: components["schemas"]["supportedCountry"]; allowedBankProviders?: components["schemas"]["allowedBankProviders"]; + /** @default false */ + isPrecalculationAllowed: boolean; }; /** @description Range of amounts allowed for a given payment method */ amountRangeRule: { @@ -659,7 +723,8 @@ export interface components { }; /** @description Value of paymentData.paymentMethodData.tokenizationData.token */ googlePayToken: string; - directTransactionIPSPData: { + /** @description In case of payment facilitator mode - this structure is mandatory */ + transactionIPSPData: { subMerchantId: string; name: string; location: string; @@ -689,6 +754,7 @@ export interface components { billingAddress?: components["schemas"]["address"]; shippingAddress?: components["schemas"]["address"]; comfortPay?: components["schemas"]["cardIdentifierOrRegister"]; + ipspData?: components["schemas"]["transactionIPSPData"]; }; /** @description The card holder name. In case of Direct API either cardHolder or email is mandatory */ cardHolder: string; @@ -808,43 +874,35 @@ export interface components { iban?: components["schemas"]["iban"]; }; /** - * @description Unstructured remittance information. At present, Tatrabanka bank transfer does not display the remittance information. SEPA remittanceInformationUnstructured contains 140 characters. For TatraPayPlus purposes, the first up to 40 characters are assigned to the paymentId. Others 100 characters are free to use + * @description Unstructured transfer information. Currently, Tatrabanka bank transfer does not display the transfer information. The entire SEPA remittanceInformationUnstructured contains 140 characters. The structure of the string consists of 3 parts: + * + * - A. paymentId - fixed 40 characters are reserved for paymentId + * - B. Merchant Name value length - provided in the contract, up to 50 characters + * - C. Custom value length - free characters are calculated as C = 140-A-(B+1). Depends on the length of the merchant name. + * + * E.g. Merchant name = MerchantABCD,s.r.o. (19 characters + 1 separator space). 80 characters are available. + * + * The result of the UnstructuredTransferInformation will look like this + * + * 07b940d2-8b82-4c7a-a0dd-5ebeabcf1f3d MerchantABCD,s.r.o. Your text in remittance + * + * If the value of RemittanceInformationUnstructured in this attribute is longer than the calculated C, your custom value will be truncated. + * + * * * @example Ref Number Merchant */ remittanceInformationUnstructured: string; }; responses: { - /** @description Successful response with access token */ - OK_200_AuthTokenSuccess: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - /** @example 20bc565f-c28c-4340-9e25-d5bef7ce1db7 */ - access_token?: string; - /** @example Bearer */ - token_type?: string; - /** @example 86400 */ - expires_in?: number; - /** @example TATRAPAYPLUS */ - scope?: string; - }; - }; - }; - /** @description Invalid client credentials */ - UNAUTHORIZED_401_AuthTokenError: { + /** @description Basic calculation provided */ + OK_200_BasicCalculation: { headers: { + "X-Request-ID": components["headers"]["X-Request-ID"]; [name: string]: unknown; }; content: { - "application/json": { - /** @example invalid_client */ - error?: string; - /** @example The given client credentials were not valid */ - error_description?: string; - }; + "application/json": components["schemas"]["basicCalculationResponse"]; }; }; /** @description TatraPayPlus Apearance set */ @@ -1076,7 +1134,7 @@ export interface components { /** * @description Timestamp. Only +/-1h from UTC(GMT) * - * @example 1092014125505 + * @example 01092014125505 */ Timestamp: string; /** @description The user id, if available. @@ -1092,18 +1150,11 @@ export interface components { "Payment-id": string; }; requestBodies: { - authTokenRequest: { + /** @description Request body for a loan precalculation. + * */ + basicCalculationRequestBody: { content: { - "application/x-www-form-urlencoded": { - /** @example client_credentials */ - grant_type: string; - /** @example dd */ - client_id: string; - /** @example dd */ - client_secret: string; - /** @example TATRAPAYPLUS */ - scope: string; - }; + "application/json": components["schemas"]["basicCalculationRequest"]; }; }; /** @description DirectAPI transaction */ @@ -1355,6 +1406,42 @@ export interface operations { 503: components["responses"]["SERVICE_UNAVAILABLE_503"]; }; }; + loanPrecalculation: { + parameters: { + query?: never; + header: { + /** + * @description ID of the request, unique to the call, as determined by the initiating party. + * @example 99391c7e-ad88-49ec-a2ad-99ddcb1f7721 + */ + "X-Request-ID": components["parameters"]["X-Request-ID"]; + /** + * @description The forwarded IP address of the user + * + * @example 192.168.8.78 + */ + "IP-Address": components["parameters"]["IP-Address"]; + }; + path?: never; + cookie?: never; + }; + requestBody?: components["requestBodies"]["basicCalculationRequestBody"]; + responses: { + 200: components["responses"]["OK_200_BasicCalculation"]; + 400: components["responses"]["BAD_REQUEST_400"]; + 401: components["responses"]["UNAUTHORIZED_401"]; + 403: components["responses"]["FORBIDDEN_403"]; + 404: components["responses"]["NOT_FOUND_404"]; + 405: components["responses"]["METHOD_NOT_ALLOWED_405"]; + 406: components["responses"]["NOT_ACCEPTABLE_406"]; + 408: components["responses"]["REQUEST_TIMEOUT_408"]; + 409: components["responses"]["CONFLICT_409"]; + 415: components["responses"]["UNSUPPORTED_MEDIA_TYPE_415"]; + 429: components["responses"]["TOO_MANY_REQUESTS_429"]; + 500: components["responses"]["INTERNAL_SERVER_ERROR_500"]; + 503: components["responses"]["SERVICE_UNAVAILABLE_503"]; + }; + }; createDirectTransactionRequest: { parameters: { query?: never; @@ -1454,17 +1541,4 @@ export interface operations { 503: components["responses"]["SERVICE_UNAVAILABLE_503"]; }; }; - getAccessToken: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: components["requestBodies"]["authTokenRequest"]; - responses: { - 200: components["responses"]["OK_200_AuthTokenSuccess"]; - 400: components["responses"]["UNAUTHORIZED_401_AuthTokenError"]; - }; - }; } diff --git a/tatrapayplus_api_sandbox.json b/tatrapayplus_api_sandbox.json index 004f214..7680f38 100644 --- a/tatrapayplus_api_sandbox.json +++ b/tatrapayplus_api_sandbox.json @@ -322,6 +322,73 @@ } } }, + "/v1/payments/loans/precalculation": { + "put": { + "summary": "Loan precalculation", + "description": "Loan precalculation", + "operationId": "loanPrecalculation", + "tags": [ + "TatraPayPlus API" + ], + "security": [ + { + "oAuth2ClientCredentials": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/X-Request-ID" + }, + { + "$ref": "#/components/parameters/IP-Address" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/basicCalculationRequestBody" + }, + "responses": { + "200": { + "$ref": "#/components/responses/OK_200_BasicCalculation" + }, + "400": { + "$ref": "#/components/responses/BAD_REQUEST_400" + }, + "401": { + "$ref": "#/components/responses/UNAUTHORIZED_401" + }, + "403": { + "$ref": "#/components/responses/FORBIDDEN_403" + }, + "404": { + "$ref": "#/components/responses/NOT_FOUND_404" + }, + "405": { + "$ref": "#/components/responses/METHOD_NOT_ALLOWED_405" + }, + "406": { + "$ref": "#/components/responses/NOT_ACCEPTABLE_406" + }, + "408": { + "$ref": "#/components/responses/REQUEST_TIMEOUT_408" + }, + "409": { + "$ref": "#/components/responses/CONFLICT_409" + }, + "415": { + "$ref": "#/components/responses/UNSUPPORTED_MEDIA_TYPE_415" + }, + "429": { + "$ref": "#/components/responses/TOO_MANY_REQUESTS_429" + }, + "500": { + "$ref": "#/components/responses/INTERNAL_SERVER_ERROR_500" + }, + "503": { + "$ref": "#/components/responses/SERVICE_UNAVAILABLE_503" + } + } + } + }, "/v1/payments-direct": { "post": { "summary": "Create direct transaction request", @@ -504,27 +571,6 @@ } } } - }, - "/auth/oauth/v2/token": { - "post": { - "summary": "Obtain OAuth2 Access Token", - "description": "Retrieves an access token using client credentials.", - "operationId": "getAccessToken", - "tags": [ - "Authentication" - ], - "requestBody": { - "$ref": "#/components/requestBodies/authTokenRequest" - }, - "responses": { - "200": { - "$ref": "#/components/responses/OK_200_AuthTokenSuccess" - }, - "400": { - "$ref": "#/components/responses/UNAUTHORIZED_401_AuthTokenError" - } - } - } } }, "components": { @@ -543,6 +589,130 @@ } }, "schemas": { + "basicCalculationResponseItem": { + "type": "object", + "required": [ + "loanInterestRate", + "installmentAmount", + "loanDuration", + "preference", + "mainPreference", + "capacityValidity", + "rpmn", + "totalAmount", + "loanFee" + ], + "properties": { + "loanInterestRate": { + "$ref": "#/components/schemas/loanInterestRate" + }, + "installmentAmount": { + "$ref": "#/components/schemas/installmentAmount" + }, + "loanDuration": { + "$ref": "#/components/schemas/loanDuration" + }, + "preference": { + "$ref": "#/components/schemas/preference" + }, + "mainPreference": { + "$ref": "#/components/schemas/mainPreference" + }, + "capacityValidity": { + "$ref": "#/components/schemas/capacityValidity" + }, + "rpmn": { + "$ref": "#/components/schemas/rpmn" + }, + "totalAmount": { + "$ref": "#/components/schemas/totalAmount" + }, + "loanFee": { + "$ref": "#/components/schemas/loanFee" + } + } + }, + "loanInterestRate": { + "type": "number", + "format": "double", + "description": "Loan interest rate" + }, + "installmentAmount": { + "type": "number", + "format": "double", + "description": "Installment amount" + }, + "loanDuration": { + "type": "integer", + "description": "Loan duration" + }, + "preference": { + "type": "boolean", + "description": "Preferred maturity of loan offer (max 3)" + }, + "mainPreference": { + "type": "boolean", + "description": "Main preferred maturity of loan offer (max 1)" + }, + "capacityValidity": { + "type": "boolean", + "description": "Loan offer is valid with respect to entered capacity" + }, + "rpmn": { + "type": "number", + "format": "double", + "description": "Calculated RPMN" + }, + "totalAmount": { + "type": "number", + "format": "double", + "description": "Total amount of the order including all fees, insurance, shipping,...", + "minimum": 100, + "maximum": 40000, + "multipleOf": 0.01, + "example": 156.95 + }, + "loanAmount": { + "type": "number", + "format": "double", + "description": "Loan amount in EUR", + "minimum": 100, + "maximum": 40000, + "multipleOf": 0.01, + "example": 156.95 + }, + "loanFee": { + "type": "number", + "format": "double" + }, + "basicCalculationResponse": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/components/schemas/basicCalculationResponseItem" + } + }, + "basicCalculationRequest": { + "type": "object", + "required": [ + "loanAmount" + ], + "properties": { + "paymentMethod": { + "description": "Only if isPrecalculationAllowed = true", + "type": "string", + "enum": [ + "PAY_LATER" + ] + }, + "loanAmount": { + "$ref": "#/components/schemas/loanAmount" + }, + "capacityInfo": { + "$ref": "#/components/schemas/capacityInfo" + } + } + }, "paymentIntentUpdateResponse": { "description": "\n**TatraPayPlus payment update response. **\n\n| selectedPaymentMethod | attribute supported |\n| ---------------- | ------------|\n| BANK_TRANSFER | N/A |\n| CARD_PAY | cardPayStatusStructure |\n| PAY_LATER | N/A |\n", "type": "object", @@ -718,7 +888,7 @@ "$ref": "#/components/schemas/directTransactionTDSData" }, "ipspData": { - "$ref": "#/components/schemas/directTransactionIPSPData" + "$ref": "#/components/schemas/transactionIPSPData" }, "token": { "$ref": "#/components/schemas/token" @@ -841,7 +1011,11 @@ "CB_NOT_FOUND", "CB_TOO_OLD", "CB_ERROR", - "NO_AVAIL_PAY_METH" + "NO_AVAIL_PAY_METH", + "LOAN_AMNT_LOW", + "LOAN_AMNT_HIGH", + "NEGATIVE_VALUE_NOT_ALLOWED", + "INSUFFICIENT_CAPACITY" ] }, "errorDescription": { @@ -1188,7 +1362,7 @@ "type": "string", "maxLength": 30, "minLength": 1, - "pattern": "^[a-zA-Z0-9Ã\u0080-ž ]{1,30}$" + "pattern": "^[a-zA-Z0-9À-ž ]{1,30}$" }, "phone": { "description": "Conditionally mandatory. In case of TatraPayPlus payment initiation - It is mandatory only if the email attribute is not provided.", @@ -1357,10 +1531,10 @@ "paymentMethodRules": { "type": "object", "required": [ - "paymentMethod" + "paymentmethod" ], "properties": { - "paymentMethod": { + "paymentmethod": { "$ref": "#/components/schemas/paymentMethod" }, "amountRangeRule": { @@ -1374,6 +1548,10 @@ }, "allowedBankProviders": { "$ref": "#/components/schemas/allowedBankProviders" + }, + "isPrecalculationAllowed": { + "type": "boolean", + "default": false } } }, @@ -1551,7 +1729,8 @@ "type": "string", "description": "Value of paymentData.paymentMethodData.tokenizationData.token" }, - "directTransactionIPSPData": { + "transactionIPSPData": { + "description": "In case of payment facilitator mode - this structure is mandatory", "type": "object", "required": [ "subMerchantId", @@ -1614,6 +1793,9 @@ }, "comfortPay": { "$ref": "#/components/schemas/cardIdentifierOrRegister" + }, + "ipspData": { + "$ref": "#/components/schemas/transactionIPSPData" } } }, @@ -1809,7 +1991,7 @@ } }, "remittanceInformationUnstructured": { - "description": "Unstructured remittance information. At present, Tatrabanka bank transfer does not display the remittance information. SEPA remittanceInformationUnstructured contains 140 characters. For TatraPayPlus purposes, the first up to 40 characters are assigned to the paymentId. Others 100 characters are free to use\n", + "description": "Unstructured transfer information. Currently, Tatrabanka bank transfer does not display the transfer information. The entire SEPA remittanceInformationUnstructured contains 140 characters. The structure of the string consists of 3 parts:\n\n - A. paymentId - fixed 40 characters are reserved for paymentId\n - B. Merchant Name value length - provided in the contract, up to 50 characters\n - C. Custom value length - free characters are calculated as C = 140-A-(B+1). Depends on the length of the merchant name.\n \n E.g. Merchant name = MerchantABCD,s.r.o. (19 characters + 1 separator space). 80 characters are available.\n \n The result of the UnstructuredTransferInformation will look like this\n \n 07b940d2-8b82-4c7a-a0dd-5ebeabcf1f3d MerchantABCD,s.r.o. Your text in remittance\n \n If the value of RemittanceInformationUnstructured in this attribute is longer than the calculated C, your custom value will be truncated. \n \n \n", "type": "string", "pattern": "^[ 0-9a-zA-Z?:()\\/\\.,'+-]{1,100}$", "maxLength": 100, @@ -1918,7 +2100,7 @@ "format": "DDMMYYYYHHMISS" }, "required": true, - "example": 1092014125505 + "example": "01092014125505" }, "User-Id": { "name": "User-Id", @@ -1972,36 +2154,12 @@ } }, "requestBodies": { - "authTokenRequest": { - "required": true, + "basicCalculationRequestBody": { + "description": "Request body for a loan precalculation.\n", "content": { - "application/x-www-form-urlencoded": { + "application/json": { "schema": { - "type": "object", - "properties": { - "grant_type": { - "type": "string", - "example": "client_credentials" - }, - "client_id": { - "type": "string", - "example": "dd" - }, - "client_secret": { - "type": "string", - "example": "dd" - }, - "scope": { - "type": "string", - "example": "TATRAPAYPLUS" - } - }, - "required": [ - "grant_type", - "client_id", - "client_secret", - "scope" - ] + "$ref": "#/components/schemas/basicCalculationRequest" } } } @@ -2062,50 +2220,17 @@ } }, "responses": { - "OK_200_AuthTokenSuccess": { - "description": "Successful response with access token", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "access_token": { - "type": "string", - "example": "20bc565f-c28c-4340-9e25-d5bef7ce1db7" - }, - "token_type": { - "type": "string", - "example": "Bearer" - }, - "expires_in": { - "type": "integer", - "example": 86400 - }, - "scope": { - "type": "string", - "example": "TATRAPAYPLUS" - } - } - } + "OK_200_BasicCalculation": { + "description": "Basic calculation provided", + "headers": { + "X-Request-ID": { + "$ref": "#/components/headers/X-Request-ID" } - } - }, - "UNAUTHORIZED_401_AuthTokenError": { - "description": "Invalid client credentials", + }, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "error": { - "type": "string", - "example": "invalid_client" - }, - "error_description": { - "type": "string", - "example": "The given client credentials were not valid" - } - } + "$ref": "#/components/schemas/basicCalculationResponse" } } } @@ -2338,8 +2463,7 @@ }, "security": [ { - "oAuth2ClientCredentials": [ - ] + "oAuth2ClientCredentials": [] } ], "tags": [ diff --git a/tests/live.test.ts b/tests/live.test.ts index da25bc1..ab0e4de 100644 --- a/tests/live.test.ts +++ b/tests/live.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { TBPlusSDK } from "../src"; +import { GatewayMode, TBPlusSDK, PaymentMethod, SimpleStatus } from "../src"; import dotenv from "dotenv"; -import { PaymentMethod, SimpleStatus } from "../src/enums"; import { getAvailable } from "../src/helpers"; import { TBPlusLogger } from "../src/logger"; @@ -216,4 +215,95 @@ describe("TBPlusSDK tests on live", () => { expect(cancelErrors2).toBeTruthy(); expect(response2.status).toBe(400); }); + + it("direct payment create", async () => { + const sdk = new TBPlusSDK( + process.env.API_KEY as string, + process.env.API_SECRET as string, + ); + const REDIRECT_URI = "http://google.com"; + const { error, response } = await sdk.createPaymentDirect( + { + amount: { + amountValue: 30, + currency: "EUR", + }, + endToEnd: { + variableSymbol: "123456", + specificSymbol: "0244763", + constantSymbol: "389", + }, + isPreAuthorization: true, + tdsData: { + cardHolder: " U5t4K7WgIgzxf9rgxt5@g4E54LhLOf@fJ", + email: "user@example.com", + phone: "+20912900552", + billingAddress: { + streetName: "Testerská", + buildingNumber: "35", + townName: "Bratislava", + postCode: "85104", + country: "SK", + }, + shippingAddress: { + streetName: "Testerská", + buildingNumber: "35", + townName: "Bratislava", + postCode: "85104", + country: "SK", + }, + }, + ipspData: { + subMerchantId: "5846864684", + name: "ASDQWE", + location: "96A6Mrz", + country: "SE", + }, + token: "ABC12345", + }, + REDIRECT_URI, + "127.0.0.1", + ); + expect(error).toBeFalsy(); + expect(response.status).toBe(201); + }); + + it("precalculate loan", async () => { + const sdk = new TBPlusSDK( + process.env.API_KEY as string, + process.env.API_SECRET as string, + {mode: GatewayMode.PRODUCTION}, + new TestLogger(), + ); + const { data, error } = await sdk.precalculateLoan( + { + paymentMethod: PaymentMethod.PAY_LATER, + loanAmount: 250.45, + capacityInfo: { + monthlyIncome: 2000, + monthlyExpenses: 800, + numberOfChildren: 0, + }, + }, + "127.0.0.1", + ); + + expect(error).toBeUndefined(); + if (!data) { + throw new Error('data empty'); + } + + expect(data).toBeInstanceOf(Array); + expect(data.length).toBeGreaterThan(0); + for (const item of data) { + expect(typeof item.mainPreference).toBe("boolean"); + expect(typeof item.preference).toBe("boolean"); + expect(typeof item.capacityValidity).toBe("boolean"); + expect(typeof item.loanInterestRate).toBe("number"); + expect(typeof item.installmentAmount).toBe("number"); + expect(typeof item.rpmn).toBe("number"); + expect(typeof item.totalAmount).toBe("number"); + expect(typeof item.loanFee).toBe("number"); + } + }); }); From b6dbc60da19657451bc3b8c80009dc120604f9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=BDubom=C3=ADr=20Samotn=C3=BD?= Date: Fri, 4 Jul 2025 15:13:41 +0200 Subject: [PATCH 2/3] triv: fix token definition --- src/paths.ts | 80 +++++++++++++++++++++++++++++++++++ tatrapayplus_api_sandbox.json | 4 +- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/paths.ts b/src/paths.ts index 0a38d3a..87a5b07 100644 --- a/src/paths.ts +++ b/src/paths.ts @@ -171,6 +171,26 @@ export interface paths { patch?: never; trace?: never; }; + "/auth/oauth/v2/token": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Obtain OAuth2 Access Token + * @description Retrieves an access token using client credentials. + */ + post: operations["getAccessToken"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; } export type webhooks = Record; export interface components { @@ -384,6 +404,7 @@ export interface components { | "LOAN_AMNT_LOW" | "LOAN_AMNT_HIGH" | "NEGATIVE_VALUE_NOT_ALLOWED" + | "INVALID_PARAMETER" | "INSUFFICIENT_CAPACITY"; errorDescription?: string; /** @description Reason codes of declined methods */ @@ -1089,6 +1110,38 @@ export interface components { }; content?: never; }; + /** @description Successful response with access token */ + OK_200_AuthTokenSuccess: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example 20bc565f-c28c-4340-9e25-d5bef7ce1db7 */ + access_token?: string; + /** @example Bearer */ + token_type?: string; + /** @example 86400 */ + expires_in?: number; + /** @example TATRAPAYPLUS */ + scope?: string; + }; + }; + }; + /** @description Invalid client credentials */ + UNAUTHORIZED_401_AuthTokenError: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example invalid_client */ + error?: string; + /** @example The given client credentials were not valid */ + error_description?: string; + }; + }; + }; }; parameters: { /** @description taskId of the asynch job @@ -1207,6 +1260,20 @@ export interface components { "application/json": components["schemas"]["appearanceLogoRequest"]; }; }; + authTokenRequest: { + content: { + "application/x-www-form-urlencoded": { + /** @example client_credentials */ + grant_type: string; + /** @example dd */ + client_id: string; + /** @example dd */ + client_secret: string; + /** @example TATRAPAYPLUS */ + scope: string; + }; + }; + }; }; headers: { /** @@ -1541,4 +1608,17 @@ export interface operations { 503: components["responses"]["SERVICE_UNAVAILABLE_503"]; }; }; + getAccessToken: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: components["requestBodies"]["authTokenRequest"]; + responses: { + 200: components["responses"]["OK_200_AuthTokenSuccess"]; + 400: components["responses"]["UNAUTHORIZED_401_AuthTokenError"]; + }; + }; } diff --git a/tatrapayplus_api_sandbox.json b/tatrapayplus_api_sandbox.json index 7680f38..8c97894 100644 --- a/tatrapayplus_api_sandbox.json +++ b/tatrapayplus_api_sandbox.json @@ -1531,10 +1531,10 @@ "paymentMethodRules": { "type": "object", "required": [ - "paymentmethod" + "paymentMethod" ], "properties": { - "paymentmethod": { + "paymentMethod": { "$ref": "#/components/schemas/paymentMethod" }, "amountRangeRule": { From 88b2cf8893467441088eb0663a549254476ac766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=BDubom=C3=ADr=20Samotn=C3=BD?= Date: Fri, 4 Jul 2025 15:26:46 +0200 Subject: [PATCH 3/3] triv: fix --- src/paths.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/paths.ts b/src/paths.ts index 87a5b07..8bd2381 100644 --- a/src/paths.ts +++ b/src/paths.ts @@ -1492,7 +1492,7 @@ export interface operations { path?: never; cookie?: never; }; - requestBody?: components["requestBodies"]["basicCalculationRequestBody"]; + requestBody: components["requestBodies"]["basicCalculationRequestBody"]; responses: { 200: components["responses"]["OK_200_BasicCalculation"]; 400: components["responses"]["BAD_REQUEST_400"];