8
8
use Magento \Framework \App \CsrfAwareActionInterface ;
9
9
use Magento \Framework \App \RequestInterface ;
10
10
use Magento \Framework \App \Request \InvalidRequestException ;
11
+ use Magento \Framework \Controller \Result \JsonFactory ;
12
+ use Magento \Framework \App \Config \ScopeConfigInterface ;
13
+ use Magento \Sales \Model \Order \Email \Sender \OrderSender ;
11
14
12
15
class Callback extends Action implements CsrfAwareActionInterface
13
16
{
14
17
protected $ order ;
15
18
protected $ khipuPayment ;
19
+ protected $ resultJsonFactory ;
20
+ protected $ scopeConfig ;
21
+ protected $ orderSender ;
16
22
17
23
public function __construct (
18
24
Context $ context ,
19
25
Order $ order ,
20
- Simplified $ khipuPayment
26
+ Simplified $ khipuPayment ,
27
+ JsonFactory $ resultJsonFactory ,
28
+ ScopeConfigInterface $ scopeConfig ,
29
+ OrderSender $ orderSender
21
30
)
22
31
{
23
32
parent ::__construct ($ context );
24
-
25
33
$ this ->order = $ order ;
26
34
$ this ->khipuPayment = $ khipuPayment ;
35
+ $ this ->resultJsonFactory = $ resultJsonFactory ;
36
+ $ this ->scopeConfig = $ scopeConfig ;
37
+ $ this ->orderSender = $ orderSender ;
27
38
}
28
39
29
40
public function createCsrfValidationException (RequestInterface $ request ): ?InvalidRequestException
@@ -36,22 +47,119 @@ public function validateForCsrf(RequestInterface $request): ?bool
36
47
return true ;
37
48
}
38
49
39
- /**
40
- * Default customer account page
41
- *
42
- * @return void
43
- */
44
50
public function execute ()
45
51
{
46
- $ order = $ this ->order ->loadByIncrementId ($ this ->getRequest ()->getParam ('order_id ' ));
52
+ $ secret = $ this ->scopeConfig ->getValue ('payment/simplified/merchant_secret ' );
53
+
54
+ if (!$ secret ) {
55
+ throw new \Exception ('Missing secret in configuration ' );
56
+ }
57
+ $ raw_post = file_get_contents ('php://input ' );
58
+ $ signature = $ _SERVER ['HTTP_X_KHIPU_SIGNATURE ' ] ?? '' ;
59
+
60
+ if (!$ signature ) {
61
+ return $ this ->resultJsonFactory ->create ()->setData (['error ' => 'Missing signature header ' ])->setStatusHeader (400 );
62
+ }
63
+
64
+ $ notificationData = json_decode ($ raw_post , true );
65
+
47
66
try {
48
- $ this ->khipuPayment ->validateKhipuCallback ($ order , $ this ->getRequest ()->getPost ()['notification_token ' ],
49
- $ this ->getRequest ()->getPost ()['api_version ' ]);
67
+ $ this ->verifyNotification ($ raw_post , $ signature , $ secret );
50
68
} catch (\Exception $ e ) {
51
- $ this ->getResponse ()->setStatusCode (\Magento \Framework \App \Response \Http::STATUS_CODE_400 );
52
- $ this ->getResponse ()->setContent ($ e ->getMessage ());
53
- return ;
69
+ return $ this ->resultJsonFactory ->create ()->setData (['error ' => $ e ->getMessage ()])->setStatusHeader (400 );
70
+ }
71
+
72
+ if (isset ($ notificationData ['payment_id ' ])) {
73
+ $ order = $ this ->order ->loadByIncrementId ($ notificationData ['transaction_id ' ]);
74
+ if ($ order ->getId ()) {
75
+ try {
76
+ $ this ->validateKhipuCallback ($ order , $ notificationData , '3.0 ' );
77
+ } catch (\Exception $ e ) {
78
+ return $ this ->resultJsonFactory ->create ()->setData (['error ' => $ e ->getMessage ()])->setStatusHeader (400 );
79
+ }
80
+ return $ this ->resultJsonFactory ->create ()->setData (['success ' => true ])->setStatusHeader (200 );
81
+ } else {
82
+ return $ this ->resultJsonFactory ->create ()->setData (['error ' => 'Order not found ' ])->setStatusHeader (404 );
83
+ }
84
+ } else {
85
+ return $ this ->resultJsonFactory ->create ()->setData (['error ' => 'Invalid notification data ' ])->setStatusHeader (400 );
54
86
}
55
- $ this ->getResponse ()->setBody ('OK ' );
87
+ }
88
+
89
+ private function verifyNotification ($ notificationData , $ signatureHeader , $ secret )
90
+ {
91
+ $ signature_parts = explode (', ' , $ signatureHeader );
92
+ $ t_value = '' ;
93
+ $ s_value = '' ;
94
+ foreach ($ signature_parts as $ part ) {
95
+ [$ key , $ value ] = explode ('= ' , $ part );
96
+ if ($ key === 't ' ) {
97
+ $ t_value = $ value ;
98
+ } elseif ($ key === 's ' ) {
99
+ $ s_value = $ value ;
100
+ }
101
+ }
102
+
103
+ $ to_hash = $ t_value . '. ' . $ notificationData ;
104
+ $ hmac_signature = hash_hmac ('sha256 ' , $ to_hash , $ secret , true );
105
+ $ hmac_base64 = base64_encode ($ hmac_signature );
106
+
107
+ return hash_equals ($ hmac_base64 , $ s_value );
108
+ }
109
+
110
+
111
+ public function validateKhipuCallback (Order $ order , $ notificationData , $ apiVersion )
112
+ {
113
+ if (!$ order || !$ order ->getIncrementId ()) {
114
+ throw new \Exception ('Order # ' . $ _REQUEST ['order_id ' ] . ' does not exist ' );
115
+ }
116
+
117
+ if ($ apiVersion != '3.0 ' ) {
118
+ throw new \Exception ('Invalid notification API version. ' );
119
+ }
120
+
121
+ if ($ notificationData ['receiver_id ' ] != $ this ->scopeConfig ->getValue ('payment/simplified/merchant_id ' )) {
122
+ throw new \Exception ('Invalid receiver ID ' );
123
+ }
124
+
125
+ if ($ notificationData ['custom ' ] != $ order ->getPayment ()->getAdditionalInformation ('khipu_order_token ' )) {
126
+ throw new \Exception ('Invalid transaction ID ' );
127
+ }
128
+
129
+ if ($ notificationData ['amount ' ] != number_format ($ order ->getGrandTotal (),
130
+ $ this ->khipuPayment ->getDecimalPlaces ($ order ->getOrderCurrencyCode ()), '. ' , '' )
131
+ ) {
132
+ throw new \Exception ('Amount mismatch ' );
133
+ }
134
+
135
+ if ($ notificationData ['currency ' ] != $ order ->getOrderCurrencyCode ()) {
136
+ throw new \Exception ('Currency mismatch ' );
137
+ }
138
+
139
+ $ responseTxt = 'Pago Khipu Aceptado<br> ' ;
140
+ $ responseTxt .= 'TransactionId: ' . $ notificationData ['transaction_id ' ] . '<br> ' ;
141
+ $ responseTxt .= 'PaymentId: ' . $ notificationData ['payment_id ' ] . '<br> ' ;
142
+ $ responseTxt .= 'Subject: ' . $ notificationData ['subject ' ] . '<br> ' ;
143
+ $ responseTxt .= 'Amount: ' . $ notificationData ['amount ' ] .' ' .$ notificationData ['currency ' ] .'<br> ' ;
144
+ $ responseTxt .= 'Body: ' . $ notificationData ['body ' ] . '<br> ' ;
145
+ $ responseTxt .= 'Bank: ' . $ notificationData ['bank ' ] . '<br> ' ;
146
+ $ responseTxt .= 'Bank Account Number: ' . $ notificationData ['bank_account_number ' ] . '<br> ' ;
147
+ $ responseTxt .= 'Payer Name: ' . $ notificationData ['payer_name ' ] . '<br> ' ;
148
+ $ responseTxt .= 'Payer Email: ' . $ notificationData ['payer_email ' ] . '<br> ' ;
149
+ $ responseTxt .= 'Personal Identifier: ' . $ notificationData ['personal_identifier ' ] . '<br> ' ;
150
+
151
+ $ invoice = $ order ->prepareInvoice ();
152
+ $ invoice ->register ();
153
+ $ invoice ->save ();
154
+
155
+ $ paymentCompleteStatus = $ this ->scopeConfig ->getValue ('payment/simplified/payment_complete_status ' );
156
+
157
+ $ order ->setState ($ paymentCompleteStatus , false , "Pago Realizado con Khipu " , true );
158
+ $ order ->setStatus ($ order ->getConfig ()->getStateDefaultStatus ($ paymentCompleteStatus ));
159
+ $ order ->setIsCustomerNotified (true );
160
+ $ order ->addStatusToHistory ($ paymentCompleteStatus , $ responseTxt );
161
+ $ order ->save ();
162
+
163
+ $ this ->orderSender ->send ($ order );
56
164
}
57
165
}
0 commit comments