Skip to content

Commit eb113fe

Browse files
OttiaOttia
authored andcommitted
createAddCardFormRequest doesn't calculate signature #55
1 parent a97be4c commit eb113fe

22 files changed

+510
-127
lines changed

RELEASE_NOTES.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Release 1.1.0
2+
3+
## What's Changed
4+
5+
**Breaking Change**: Refactored `createAddCardFormRequest` to internalize signature calculation and authentication parameters. This aligns the method with the rest of the SDK patterns and simplifies usage for developers. The `AddCardFormRequest` model no longer requires `checkoutAccount`, `checkoutMethod`, `checkoutNonce`, `checkoutTimestamp`, `checkoutAlgorithm`, or `signature` to be populated manually.
6+
7+
### Features
8+
- Internalized signature calculation for `createAddCardFormRequest` (matches `createPayment` pattern).
9+
- Implemented Form Post style authentication (Body Auth) for add-card requests.
10+
11+
### Refactoring
12+
- Removed manual authentication fields from `AddCardFormRequest` model.
13+
- Automatically injects merchant credentials and generates HMAC signature within the SDK.
14+
15+
### Testing
16+
- Added unit tests to verify signature generation and payload structure for card form requests.

TESTING.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Testing Guide
2+
3+
This guide describes how to test the `paytrail-js-sdk` locally, focusing on the recent changes to `createAddCardFormRequest`.
4+
5+
## Prerequisites
6+
7+
- Node.js (>= 20)
8+
- npm
9+
10+
## Running Automated Tests
11+
12+
To run the unit tests, use the following command:
13+
14+
```bash
15+
npm test
16+
```
17+
18+
To run only the tests related to `createAddCardFormRequest`:
19+
20+
```bash
21+
npm test -- tests/create-add-card-form.test.ts
22+
```
23+
24+
## Manual / Integration Verification
25+
26+
### Setup
27+
1. Ensure you have valid Paytrail Merchant credentials (Merchant ID and Secret Key).
28+
2. The current test suite uses default test credentials (`merchantId: 695861`, `secret: MONISAIPPUAKAUPPIAS`).
29+
30+
### Test Flow
31+
1. Instantiate `PaytrailClient` with your credentials.
32+
2. Create an `AddCardFormRequest` object with only the business fields:
33+
- `checkoutRedirectSuccessUrl`
34+
- `checkoutRedirectCancelUrl`
35+
- `language` (optional)
36+
- `checkoutCallbackSuccessUrl` (optional)
37+
- `checkoutCallbackCancelUrl` (optional)
38+
3. Call `client.createAddCardFormRequest(request)`.
39+
4. Inspect the result. It should contain a `redirectUrl`.
40+
41+
### Example Code
42+
43+
```typescript
44+
import { PaytrailClient, AddCardFormRequest } from '@paytrail/paytrail-js-sdk';
45+
46+
const client = new PaytrailClient({
47+
merchantId: 1072377,
48+
secretKey: '226382458d7a1a75486c9732881472dd61fff6debfb193afab1f7e8b85131081418380be827a69fc',
49+
platformName: 'test'
50+
});
51+
52+
const run = async () => {
53+
const request = new AddCardFormRequest();
54+
request.checkoutRedirectSuccessUrl = 'https://example.com/success';
55+
request.checkoutRedirectCancelUrl = 'https://example.com/cancel';
56+
request.language = 'EN';
57+
58+
try {
59+
const response = await client.createAddCardFormRequest(request);
60+
console.log('Redirect URL:', response.data.redirectUrl);
61+
} catch (error) {
62+
console.error('Error:', error);
63+
}
64+
};
65+
66+
run();
67+
```

dist/models/request/add-card-form.model.d.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,23 @@ export declare class AddCardFormRequest {
88
/**
99
* Paytrail account ID.
1010
*/
11-
checkoutAccount: number;
11+
checkoutAccount?: number;
1212
/**
1313
* Used signature algorithm. The same as used by merchant when creating the payment.
1414
*/
15-
checkoutAlgorithm: string;
15+
checkoutAlgorithm?: string;
1616
/**
1717
* HTTP verb of the request. Always POST for addcard-form.
1818
*/
19-
checkoutMethod: string;
19+
checkoutMethod?: string;
2020
/**
2121
* Unique identifier for this request.
2222
*/
23-
checkoutNonce: string;
23+
checkoutNonce?: string;
2424
/**
2525
* ISO 8601 date time.
2626
*/
27-
checkoutTimestamp: string;
27+
checkoutTimestamp?: string;
2828
/**
2929
* Merchant's url for user redirect on successful card addition.
3030
*/
@@ -36,7 +36,7 @@ export declare class AddCardFormRequest {
3636
/**
3737
* Signature calculated from 'checkout-' prefixed POST parameters the same way as calculating signature from headers.
3838
*/
39-
signature: string;
39+
signature?: string;
4040
/**
4141
* Merchant's url called on successful card addition.
4242
*/

dist/models/request/add-card-form.model.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,31 @@ exports.AddCardFormRequest = AddCardFormRequest;
2323
__decorate([
2424
(0, class_validator_1.IsNotEmpty)(),
2525
(0, class_validator_1.IsNumber)(),
26+
(0, class_validator_1.IsOptional)(),
2627
__metadata("design:type", Number)
2728
], AddCardFormRequest.prototype, "checkoutAccount", void 0);
2829
__decorate([
2930
(0, class_validator_1.IsNotEmpty)(),
3031
(0, class_validator_1.IsString)(),
32+
(0, class_validator_1.IsOptional)(),
3133
__metadata("design:type", String)
3234
], AddCardFormRequest.prototype, "checkoutAlgorithm", void 0);
3335
__decorate([
3436
(0, class_validator_1.IsNotEmpty)(),
3537
(0, class_validator_1.IsString)(),
38+
(0, class_validator_1.IsOptional)(),
3639
__metadata("design:type", String)
3740
], AddCardFormRequest.prototype, "checkoutMethod", void 0);
3841
__decorate([
3942
(0, class_validator_1.IsNotEmpty)(),
4043
(0, class_validator_1.IsString)(),
44+
(0, class_validator_1.IsOptional)(),
4145
__metadata("design:type", String)
4246
], AddCardFormRequest.prototype, "checkoutNonce", void 0);
4347
__decorate([
4448
(0, class_validator_1.IsNotEmpty)(),
4549
(0, class_validator_1.IsString)(),
50+
(0, class_validator_1.IsOptional)(),
4651
__metadata("design:type", String)
4752
], AddCardFormRequest.prototype, "checkoutTimestamp", void 0);
4853
__decorate([
@@ -58,6 +63,7 @@ __decorate([
5863
__decorate([
5964
(0, class_validator_1.IsNotEmpty)(),
6065
(0, class_validator_1.IsString)(),
66+
(0, class_validator_1.IsOptional)(),
6167
__metadata("design:type", String)
6268
], AddCardFormRequest.prototype, "signature", void 0);
6369
__decorate([

dist/models/request/create-payment.model.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const callback_url_model_1 = require("./request-model/callback-url.model");
1818
const payment_method_group_model_1 = require("./request-model/payment-method-group.model");
1919
const class_transformer_1 = require("class-transformer");
2020
require("reflect-metadata");
21+
const create_payment_validator_1 = require("../../validators/create-payment.validator");
2122
/**
2223
* Class CreatePaymentRequest
2324
*
@@ -66,6 +67,7 @@ __decorate([
6667
(0, class_validator_1.IsOptional)(),
6768
(0, class_validator_1.ValidateNested)({ each: true }),
6869
(0, class_transformer_1.Type)(() => item_model_1.Item),
70+
(0, create_payment_validator_1.ValidateItemPrices)(),
6971
__metadata("design:type", Array)
7072
], CreatePaymentRequest.prototype, "items", void 0);
7173
__decorate([

dist/models/request/request-model/item.model.d.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Commission } from './commission.model';
21
import 'reflect-metadata';
32
/**
43
* Class Item
@@ -11,6 +10,9 @@ export declare class Item {
1110
/**
1211
* Price per unit, VAT included, in each country's
1312
* minor unit, e.g. for Euros use cents.
13+
* Min value -2147483648, max value 2147483647.
14+
* Negative values are not allowed when usePricesWithoutVat is true
15+
* or for Shop-in-Shop items.
1416
*/
1517
unitPrice: number;
1618
/**
@@ -61,9 +63,4 @@ export declare class Item {
6163
* Required for Shop-in-Shop payments, do not use for normal payments.
6264
*/
6365
merchant?: string;
64-
/**
65-
* Shop-in-Shop commission.
66-
* Do not use for normal payments.
67-
*/
68-
commission?: Commission;
6966
}

dist/models/request/request-model/item.model.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ var __metadata = (this && this.__metadata) || function (k, v) {
1111
Object.defineProperty(exports, "__esModule", { value: true });
1212
exports.Item = void 0;
1313
const class_validator_1 = require("class-validator");
14-
const commission_model_1 = require("./commission.model");
15-
const class_transformer_1 = require("class-transformer");
1614
require("reflect-metadata");
1715
/**
1816
* Class Item
@@ -27,7 +25,6 @@ exports.Item = Item;
2725
__decorate([
2826
(0, class_validator_1.IsNotEmpty)(),
2927
(0, class_validator_1.IsNumber)(),
30-
(0, class_validator_1.Min)(0),
3128
__metadata("design:type", Number)
3229
], Item.prototype, "unitPrice", void 0);
3330
__decorate([
@@ -82,9 +79,3 @@ __decorate([
8279
(0, class_validator_1.Length)(0, 50),
8380
__metadata("design:type", String)
8481
], Item.prototype, "merchant", void 0);
85-
__decorate([
86-
(0, class_validator_1.IsOptional)(),
87-
(0, class_validator_1.ValidateNested)(),
88-
(0, class_transformer_1.Type)(() => commission_model_1.Commission),
89-
__metadata("design:type", commission_model_1.Commission)
90-
], Item.prototype, "commission", void 0);

dist/models/request/request-model/shop-in-shop-item.model.d.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1+
import { Item } from './item.model';
12
import { Commission } from './commission.model';
23
import 'reflect-metadata';
34
/**
45
* Class ShopInShopItem
6+
*
7+
* Shop-in-Shop item extends the base Item class with specific validations:
8+
* - stamp is required
9+
* - reference is required
10+
* - merchant is required
11+
* - commission can be given but is optional
12+
* - unitPrice minimum is 0 (no negative values allowed)
513
*/
6-
export declare class ShopInShopItem {
14+
export declare class ShopInShopItem extends Item {
715
/**
8-
* Item level order ID (suborder ID). Mainly useful for Shop-in-Shop purchases.
16+
* Price per unit, VAT included, in each country's
17+
* minor unit, e.g. for Euros use cents.
18+
* For Shop-in-Shop items, negative values are not allowed.
919
*/
10-
orderId?: string;
20+
unitPrice: number;
1121
/**
1222
* Unique identifier for this item. Required for Shop-in-Shop payments. Required for item refunds.
1323
*/
@@ -21,7 +31,7 @@ export declare class ShopInShopItem {
2131
*/
2232
merchant: string;
2333
/**
24-
* Shop-in-Shop commission. Do not use for normal payments.
34+
* Shop-in-Shop commission. Optional for Shop-in-Shop payments only.
2535
*/
2636
commission?: Commission;
2737
}

dist/models/request/request-model/shop-in-shop-item.model.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,29 @@ var __metadata = (this && this.__metadata) || function (k, v) {
1111
Object.defineProperty(exports, "__esModule", { value: true });
1212
exports.ShopInShopItem = void 0;
1313
const class_validator_1 = require("class-validator");
14-
const commission_model_1 = require("./commission.model");
14+
const item_model_1 = require("./item.model");
1515
const class_transformer_1 = require("class-transformer");
16+
const commission_model_1 = require("./commission.model");
1617
require("reflect-metadata");
1718
/**
1819
* Class ShopInShopItem
20+
*
21+
* Shop-in-Shop item extends the base Item class with specific validations:
22+
* - stamp is required
23+
* - reference is required
24+
* - merchant is required
25+
* - commission can be given but is optional
26+
* - unitPrice minimum is 0 (no negative values allowed)
1927
*/
20-
class ShopInShopItem {
28+
class ShopInShopItem extends item_model_1.Item {
2129
}
2230
exports.ShopInShopItem = ShopInShopItem;
2331
__decorate([
24-
(0, class_validator_1.IsOptional)(),
25-
(0, class_validator_1.IsString)(),
26-
__metadata("design:type", String)
27-
], ShopInShopItem.prototype, "orderId", void 0);
32+
(0, class_validator_1.IsNumber)(),
33+
(0, class_validator_1.Min)(0),
34+
(0, class_validator_1.Max)(2147483647),
35+
__metadata("design:type", Number)
36+
], ShopInShopItem.prototype, "unitPrice", void 0);
2837
__decorate([
2938
(0, class_validator_1.IsNotEmpty)(),
3039
__metadata("design:type", String)
@@ -38,6 +47,7 @@ __decorate([
3847
__metadata("design:type", String)
3948
], ShopInShopItem.prototype, "merchant", void 0);
4049
__decorate([
50+
(0, class_validator_1.IsOptional)(),
4151
(0, class_validator_1.ValidateNested)(),
4252
(0, class_transformer_1.Type)(() => commission_model_1.Commission),
4353
__metadata("design:type", commission_model_1.Commission)

dist/models/response/add-card-form.model.d.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ export declare class AddCardFormResponse extends Response {
1212
* Class AddCardFormData
1313
*/
1414
export declare class AddCardFormData {
15-
/**
16-
* Assigned transaction ID for the payment.
17-
*/
18-
transactionId: string;
1915
/**
2016
* URL to hosted payment gateway.
2117
*/

0 commit comments

Comments
 (0)