Skip to content

Commit 56c5735

Browse files
authored
Merge pull request thephpleague#169 from anush/level3
Support for level 3 data
2 parents f7d4171 + 4faa2cd commit 56c5735

File tree

3 files changed

+156
-9
lines changed

3 files changed

+156
-9
lines changed

src/Message/AuthorizeRequest.php

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
*/
66
namespace Omnipay\Stripe\Message;
77

8+
use Money\Formatter\DecimalMoneyFormatter;
9+
use Omnipay\Common\Exception\InvalidRequestException;
810
use Omnipay\Common\ItemBag;
11+
use Omnipay\Stripe\StripeItem;
912
use Omnipay\Stripe\StripeItemBag;
10-
use Money\Formatter\DecimalMoneyFormatter;
1113

1214
/**
1315
* Stripe Authorize Request.
@@ -155,7 +157,7 @@ public function setOnBehalfOf($value)
155157

156158
/**
157159
* @return string
158-
* @throws \Omnipay\Common\Exception\InvalidRequestException
160+
* @throws InvalidRequestException
159161
*/
160162
public function getApplicationFee()
161163
{
@@ -174,7 +176,7 @@ public function getApplicationFee()
174176
* Get the payment amount as an integer.
175177
*
176178
* @return integer
177-
* @throws \Omnipay\Common\Exception\InvalidRequestException
179+
* @throws InvalidRequestException
178180
*/
179181
public function getApplicationFeeInteger()
180182
{
@@ -231,7 +233,7 @@ public function setReceiptEmail($email)
231233
/**
232234
* A list of items in this order
233235
*
234-
* @return ItemBag|null A bag containing items in this order
236+
* @return StripeItemBag|StripeItem[]|null A bag containing items in this order
235237
*/
236238
public function getItems()
237239
{
@@ -242,6 +244,7 @@ public function getItems()
242244
* Set the items in this order
243245
*
244246
* @param array $items An array of items in this order
247+
* @return AuthorizeRequest
245248
*/
246249
public function setItems($items)
247250
{
@@ -265,11 +268,40 @@ public function getData()
265268
$data['capture'] = 'false';
266269

267270
if ($items = $this->getItems()) {
268-
$itemDescriptions = [];
269-
foreach ($items as $n => $item) {
270-
$itemDescriptions[] = $item->getDescription();
271+
if (empty($this->getDescription())) {
272+
$itemDescriptions = [];
273+
foreach ($items as $n => $item) {
274+
$itemDescriptions[] = $item->getDescription();
275+
}
276+
$data['description'] = implode(" + ", $itemDescriptions);
277+
}
278+
279+
if ($this->validateLineItemsForLevel3($items)) {
280+
$lineItems = [];
281+
foreach ($items as $item) {
282+
$lineItem = [
283+
'product_code' => substr($item->getName(), 0, 12),
284+
'product_description' => substr($item->getDescription(), 0, 26)
285+
];
286+
if ($item->getPrice()) {
287+
$lineItem['unit_cost'] = $this->getAmountWithCurrencyPrecision($item->getPrice());
288+
}
289+
if ($item->getQuantity()) {
290+
$lineItem['quantity'] = $item->getQuantity();
291+
}
292+
if ($item->getTaxes()) {
293+
$lineItem['tax_amount'] = $this->getAmountWithCurrencyPrecision($item->getTaxes());
294+
}
295+
if ($item->getDiscount()) {
296+
$lineItem['discount_amount'] = $this->getAmountWithCurrencyPrecision($item->getDiscount());
297+
}
298+
$lineItems[] = $lineItem;
299+
}
300+
$data['level3'] = [
301+
'merchant_reference' => $this->getTransactionId(),
302+
'line_items' => $lineItems
303+
];
271304
}
272-
$data['description'] = implode(" + ", $itemDescriptions);
273305
}
274306

275307
if ($this->getStatementDescriptor()) {
@@ -319,8 +351,29 @@ public function getData()
319351
return $data;
320352
}
321353

354+
private function getAmountWithCurrencyPrecision($amount)
355+
{
356+
return (int)round($amount * pow(10, $this->getCurrencyDecimalPlaces()));
357+
}
358+
359+
/**
360+
* For Stripe to accept Level 3 data, the sum of all the line items should equal the request's `amount`. This
361+
* method validates that the summation adds up as expected.
362+
*
363+
* @param StripeItemBag $items
364+
* @return bool
365+
*/
366+
private function validateLineItemsForLevel3(StripeItemBag $items)
367+
{
368+
$actualAmount = 0;
369+
foreach ($items as $item) {
370+
$actualAmount += $item->getQuantity() * $item->getPrice() + $item->getTaxes() - $item->getDiscount();
371+
}
372+
return (string)$actualAmount == (string)$this->getAmount();
373+
}
374+
322375
public function getEndpoint()
323376
{
324-
return $this->endpoint.'/charges';
377+
return $this->endpoint . '/charges';
325378
}
326379
}

src/StripeItem.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,23 @@
1414
*/
1515
class StripeItem extends Item
1616
{
17+
public function getTaxes()
18+
{
19+
return $this->getParameter('taxes');
20+
}
1721

22+
public function setTaxes($value)
23+
{
24+
$this->setParameter('taxes', $value);
25+
}
26+
27+
public function getDiscount()
28+
{
29+
return $this->getParameter('discount');
30+
}
31+
32+
public function setDiscount($value)
33+
{
34+
$this->setParameter('discount', $value);
35+
}
1836
}

tests/Message/AuthorizeRequestTest.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,82 @@ public function testGetData()
4242
$this->assertSame(100, $data['application_fee']);
4343
}
4444

45+
public function testDataWithLevel3()
46+
{
47+
$this->request->setItems([
48+
[
49+
'name' => 'Cupcakes',
50+
'description' => 'Yummy Cupcakes',
51+
'price' => 4,
52+
'quantity' => 2,
53+
'taxes' => 0.4
54+
],
55+
[
56+
'name' => 'Donuts',
57+
'description' => 'A dozen donuts',
58+
'price' => 1.5,
59+
'quantity' => 12,
60+
'discount' => 1.8,
61+
'taxes' => 0.81
62+
]
63+
]);
64+
$this->request->setTransactionId('ORD42-P1');
65+
$this->request->setAmount(25.41);
66+
67+
$data = $this->request->getData();
68+
69+
$this->assertSame('Order #42', $data['description']);
70+
$expectedLevel3 = [
71+
'merchant_reference' => 'ORD42-P1',
72+
'line_items' => [
73+
[
74+
'product_code' => 'Cupcakes',
75+
'product_description' => 'Yummy Cupcakes',
76+
'unit_cost' => 400,
77+
'quantity' => 2,
78+
'tax_amount' => 40,
79+
],
80+
[
81+
'product_code' => 'Donuts',
82+
'product_description' => 'A dozen donuts',
83+
'unit_cost' => 150,
84+
'quantity' => 12,
85+
'discount_amount' => 180,
86+
'tax_amount' => 81,
87+
]
88+
]
89+
];
90+
$this->assertEquals($expectedLevel3, $data['level3']);
91+
}
92+
93+
public function testDataWithInvalidLevel3()
94+
{
95+
$this->request->setItems([
96+
[
97+
'name' => 'Cupcakes',
98+
'description' => 'Yummy Cupcakes',
99+
'price' => 4,
100+
'quantity' => 2,
101+
'taxes' => 0.4
102+
],
103+
[
104+
'name' => 'Donuts',
105+
'description' => 'A dozen donuts',
106+
'price' => 1.5,
107+
'quantity' => 12,
108+
'discount' => 1.8,
109+
'taxes' => 0.8
110+
]
111+
]);
112+
$this->request->setTransactionId('ORD42-P1');
113+
$this->request->setAmount(25.41);
114+
115+
$data = $this->request->getData();
116+
117+
$this->assertArrayNotHasKey('level3', $data,
118+
'should not include level 3 data if the line items do not add up to the amount');
119+
}
120+
45121
/**
46122
* @expectedException \Omnipay\Common\Exception\InvalidRequestException
47123
* @expectedExceptionMessage The source parameter is required

0 commit comments

Comments
 (0)