Skip to content

Commit b057b7a

Browse files
authored
Merge pull request #402 from mercadopago/feature/orders_3ds
feat: adding 3ds feature
2 parents 5ab1234 + 220eb5f commit b057b7a

6 files changed

Lines changed: 636 additions & 1 deletion

File tree

e2e/order/create3DS.spec.ts

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
import MercadoPago from '@src/index';
2+
import { config } from '../e2e.config';
3+
import { Order } from '@src/clients/order';
4+
import { OrderCreateData } from '@src/clients/order/create/types';
5+
6+
describe('Create Order with 3DS integration test', () => {
7+
let mercadoPagoConfig: MercadoPago;
8+
let order: Order;
9+
10+
beforeEach(() => {
11+
mercadoPagoConfig = new MercadoPago({ accessToken: config.access_token });
12+
order = new Order(mercadoPagoConfig);
13+
});
14+
15+
test('should create Order with 3DS on_fraud_risk validation', async () => {
16+
const body: OrderCreateData = {
17+
body: {
18+
type: 'online',
19+
processing_mode: 'automatic',
20+
total_amount: '150.00',
21+
external_reference: '3ds_test_fraud_risk',
22+
config: {
23+
online: {
24+
transaction_security: {
25+
validation: 'on_fraud_risk',
26+
liability_shift: 'required'
27+
}
28+
}
29+
},
30+
transactions: {
31+
payments: [
32+
{
33+
amount: '150.00',
34+
payment_method: {
35+
id: 'master',
36+
type: 'credit_card',
37+
token: 'CARD_TOKEN', // This should be replaced with a valid test token
38+
installments: 1
39+
}
40+
}
41+
]
42+
},
43+
payer: {
44+
email: 'test_3ds@testuser.com',
45+
identification: {
46+
type: 'CPF',
47+
number: '12345678901'
48+
}
49+
}
50+
}
51+
};
52+
53+
const response = await order.create(body);
54+
55+
expect(response.id).toBeTruthy();
56+
expect(response.type).toBe('online');
57+
expect(response.total_amount).toBe('150.00');
58+
expect(response.external_reference).toBe('3ds_test_fraud_risk');
59+
60+
// Verify 3DS configuration
61+
expect(response.config?.online?.transaction_security?.validation).toBe('on_fraud_risk');
62+
expect(response.config?.online?.transaction_security?.liability_shift).toBe('required');
63+
64+
// Verify payment structure
65+
const payment = response.transactions?.payments?.[0];
66+
expect(payment).toBeTruthy();
67+
expect(payment?.amount).toBe('150.00');
68+
expect(payment?.payment_method?.id).toBe('master');
69+
expect(payment?.payment_method?.type).toBe('credit_card');
70+
71+
// Check if 3DS transaction security is present in payment method
72+
if (payment?.payment_method?.transaction_security) {
73+
expect(payment.payment_method.transaction_security.validation).toBe('on_fraud_risk');
74+
expect(payment.payment_method.transaction_security.liability_shift).toBe('required');
75+
}
76+
});
77+
78+
test('should create Order with 3DS always validation', async () => {
79+
const body: OrderCreateData = {
80+
body: {
81+
type: 'online',
82+
processing_mode: 'automatic',
83+
total_amount: '200.00',
84+
external_reference: '3ds_test_always',
85+
config: {
86+
online: {
87+
transaction_security: {
88+
validation: 'always',
89+
liability_shift: 'preferred'
90+
}
91+
}
92+
},
93+
transactions: {
94+
payments: [
95+
{
96+
amount: '200.00',
97+
payment_method: {
98+
id: 'visa',
99+
type: 'credit_card',
100+
token: 'CARD_TOKEN', // This should be replaced with a valid test token
101+
installments: 1
102+
}
103+
}
104+
]
105+
},
106+
payer: {
107+
email: 'test_3ds_always@testuser.com',
108+
identification: {
109+
type: 'CPF',
110+
number: '12345678901'
111+
}
112+
}
113+
}
114+
};
115+
116+
const response = await order.create(body);
117+
118+
expect(response.id).toBeTruthy();
119+
expect(response.config?.online?.transaction_security?.validation).toBe('always');
120+
expect(response.config?.online?.transaction_security?.liability_shift).toBe('preferred');
121+
});
122+
123+
test('should create Order with 3DS never validation', async () => {
124+
const body: OrderCreateData = {
125+
body: {
126+
type: 'online',
127+
processing_mode: 'automatic',
128+
total_amount: '100.00',
129+
external_reference: '3ds_test_never',
130+
config: {
131+
online: {
132+
transaction_security: {
133+
validation: 'never',
134+
liability_shift: 'required'
135+
}
136+
}
137+
},
138+
transactions: {
139+
payments: [
140+
{
141+
amount: '100.00',
142+
payment_method: {
143+
id: 'master',
144+
type: 'credit_card',
145+
token: 'CARD_TOKEN', // This should be replaced with a valid test token
146+
installments: 1
147+
}
148+
}
149+
]
150+
},
151+
payer: {
152+
email: 'test_3ds_never@testuser.com',
153+
identification: {
154+
type: 'CPF',
155+
number: '12345678901'
156+
}
157+
}
158+
}
159+
};
160+
161+
const response = await order.create(body);
162+
163+
expect(response.id).toBeTruthy();
164+
expect(response.config?.online?.transaction_security?.validation).toBe('never');
165+
166+
// With 'never' validation, payment should be processed directly without 3DS
167+
const payment = response.transactions?.payments?.[0];
168+
expect(payment?.status).toBeDefined();
169+
});
170+
171+
test('should handle 3DS challenge response correctly', async () => {
172+
// First create an order that might require 3DS challenge
173+
const createBody: OrderCreateData = {
174+
body: {
175+
type: 'online',
176+
processing_mode: 'automatic',
177+
total_amount: '150.00',
178+
external_reference: '3ds_challenge_test',
179+
config: {
180+
online: {
181+
transaction_security: {
182+
validation: 'on_fraud_risk',
183+
liability_shift: 'required'
184+
}
185+
}
186+
},
187+
transactions: {
188+
payments: [
189+
{
190+
amount: '150.00',
191+
payment_method: {
192+
id: 'master',
193+
type: 'credit_card',
194+
token: 'CARD_TOKEN', // Use test token that triggers challenge
195+
installments: 1
196+
}
197+
}
198+
]
199+
},
200+
payer: {
201+
email: 'test_3ds_challenge@testuser.com',
202+
identification: {
203+
type: 'CPF',
204+
number: '12345678901'
205+
}
206+
}
207+
}
208+
};
209+
210+
const createResponse = await order.create(createBody);
211+
expect(createResponse.id).toBeTruthy();
212+
213+
// Get the order to check final status
214+
const getResponse = await order.get({ id: createResponse.id! });
215+
216+
expect(getResponse.id).toBe(createResponse.id);
217+
expect(getResponse.transactions?.payments).toBeDefined();
218+
219+
const payment = getResponse.transactions?.payments?.[0];
220+
expect(payment).toBeTruthy();
221+
222+
// Verify that 3DS information is preserved in the response
223+
if (payment?.payment_method?.transaction_security) {
224+
expect(['always', 'on_fraud_risk', 'never']).toContain(
225+
payment.payment_method.transaction_security.validation
226+
);
227+
expect(['required', 'preferred']).toContain(
228+
payment.payment_method.transaction_security.liability_shift
229+
);
230+
}
231+
232+
// Check possible 3DS statuses
233+
if (payment?.status === 'action_required' && payment?.status_detail === 'pending_challenge') {
234+
// 3DS challenge is required
235+
expect(payment.payment_method?.transaction_security?.url).toBeTruthy();
236+
console.log('3DS Challenge URL:', payment.payment_method?.transaction_security?.url);
237+
} else if (payment?.status === 'processed') {
238+
// Payment was processed without challenge or after successful challenge
239+
expect(payment.status_detail).toBeDefined();
240+
}
241+
});
242+
});

0 commit comments

Comments
 (0)