-
Notifications
You must be signed in to change notification settings - Fork 0
/
commerce_dragonpay.module
556 lines (488 loc) · 18.2 KB
/
commerce_dragonpay.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
<?php
/**
* @file
* Implements Dragonpay payment in Drupal Commerce checkout.
*/
/**
* Implements hook_menu().
*/
function commerce_dragonpay_menu() {
$items = array();
$items['commerce_dragonpay/postback'] = array(
'page callback' => 'commerce_dragonpay_process_postback',
'page arguments' => array(),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Dragonpay postback handler.
*/
function commerce_dragonpay_process_postback() {
// Load the parameters.
$params = drupal_get_query_parameters();
// Load the DragonPay rules settings.
$settings = commerce_dragonpay_rule_get_action_settings();
if (empty($params)) {
$params = $_POST;
}
if ($settings['log_status'] && !empty($params)) {
$url = url('commerce_dragonpay/postback', array('query' => $params, 'absolute' => TRUE));
watchdog('dragonpay_debug', 'POST: unserialize(\'' . serialize($params) . '\') <br />' . $url);
}
if (isset($params['txnid']) && !empty($params['txnid']) && isset($params['digest']) && !empty($params['digest'])) {
// Move and remove the digest in the array.
$digest = $params['digest'];
// Compare the Digest with the order.
if ($digest == commerce_dragonpay_generate_digest_key($params, $settings['secretkey'])) {
$redirect = '';
$status_codes = _commerce_dragonpay_status_codes();
$error_codes = _commerce_dragonpay_error_codes();
$remarks = _commerce_dragonpay_extract_error_code($params['message']);
$remarks_id = isset($error_codes[$remarks['id']]) ? $error_codes[$remarks['id']] : 'Unknow Error ID';
$remarks_info = isset($remarks['info']) ? $remarks['info'] : $params['message'];
$status_code = isset($status_codes[$params['status']]) ? $status_codes[$params['status']] : 'Unknow Status';
// Extract order ID from txnid.
$order_id = commerce_dragonpay_extract_orderid_from_txnid($params['txnid']);
if ($order = commerce_order_load($order_id)) {
$statuses = commerce_order_statuses();
switch ($params['status']) {
case 'S':
// Complete the order when the state used is checkout.
if (isset($statuses[$order->status]['state']) && $statuses[$order->status]['state'] == 'checkout') {
rules_invoke_all('commerce_checkout_complete', $order);
}
// Redirect to checkout complete page if logged-in.
if ($GLOBALS['user']->uid == $order->uid) {
$redirect = 'checkout/' . $order->order_id . '/complete';
}
break;
case 'P':
// Keep the payment on checkout_payment until verified.
// commerce_order_status_update($order, 'pending', FALSE, TRUE, filter_xss($remarks_id . ': ' . $remarks_info));
drupal_set_message(t('@info', array('@id' => $remarks_id, '@info' => $remarks_info)));
break;
case 'F':
case 'U':
case 'K':
case 'V':
// Revert the order status to cart.
commerce_order_status_update($order, 'checkout', FALSE, TRUE, filter_xss($remarks_id . ': ' . $remarks_info));
// Redirect to cart.
$redirect = ($GLOBALS['user']->uid == $order->uid) ? 'checkout' : '';
drupal_set_message(t('"@id" - "@info"', array('@id' => $remarks_id, '@info' => $remarks_info)), 'error');
break;
}
}
if ($settings['log_status']) {
watchdog('dragonpay_payment', '@txnid - Status: "@status", Message: "@id: @info"', array(
'@txnid' => $params['txnid'],
'@status' => $status_code,
'@id' => $remarks_id,
'@info' => $remarks_info,
)
);
}
drupal_goto($redirect);
}
}
}
/**
* Returns the action settings.
*/
function commerce_dragonpay_rule_get_action_settings() {
$rule = rules_config_load('commerce_payment_dragonpay');
foreach ($rule->actions() as $action) {
if (isset($action->settings['payment_method']) && $action->settings['payment_method']['method_id'] == 'dragonpay') {
return $action->settings['payment_method']['settings'];
}
}
}
/**
* Implements hook_commerce_payment_method_info().
*/
function commerce_dragonpay_commerce_payment_method_info() {
$payment_methods = array();
$payment_methods['dragonpay'] = array(
'base' => 'commerce_dragonpay',
'title' => t('Dragonpay'),
'short_title' => t('Dragonpay'),
'description' => t('Dragonpay Standard Payments'),
'terminal' => FALSE,
'offsite' => TRUE,
'offsite_autoredirect' => TRUE,
'buttonsource' => 'Commerce_dragonpay',
);
return $payment_methods;
}
/**
* Returns the default settings for the Dragonpay standard payment method.
*/
function commerce_dragonpay_default_settings() {
return array(
'merchantid' => '',
'secretkey' => '',
'currency_code' => 'PHP',
'server' => 'sandbox',
'log_status' => '',
'txnid' => 'U[commerce-order:uid]O[commerce-order:order-id]D[commerce-order:changed:raw]',
'description' => 'Order #[commerce-order:order-id] at [site:name]',
);
}
/**
* Payment method callback: settings form.
*/
function commerce_dragonpay_settings_form($settings = array()) {
$form = array();
// Merge default settings into the stored settings array.
$settings = (array) $settings + commerce_dragonpay_default_settings();
$form['merchantid'] = array(
'#type' => 'textfield',
'#title' => t('Merchant ID'),
'#description' => t('A unique code assigned to Merchant. Register at Dragonpay.ph to get your Merchant ID'),
'#default_value' => $settings['merchantid'],
'#required' => TRUE,
);
$form['secretkey'] = array(
'#type' => 'textfield',
'#title' => t('Secret Key / Password'),
'#description' => t('A unique password assigned to Merchant for checksum validation.'),
'#default_value' => $settings['secretkey'],
'#required' => TRUE,
);
$form['server'] = array(
'#type' => 'radios',
'#title' => t('Dragonpay server'),
'#options' => array(
'sandbox' => ('Sandbox - use for testing, requires a Dragonpay Test account'),
'live' => ('Live - use for processing real transactions'),
),
'#default_value' => $settings['server'],
'#required' => TRUE,
);
$form['currency_code'] = array(
'#type' => 'select',
'#title' => t('Default currency'),
'#description' => t('Transactions in other currencies will be converted to this currency. Commerce Multicurrency module must be enabled and configured to use this functionality.'),
'#options' => commerce_dragonpay_currencies(),
'#default_value' => $settings['currency_code'],
'#disabled' => TRUE,
);
$form['description'] = array(
'#type' => 'textfield',
'#title' => t('Description'),
'#description' => t('A brief description of what the payment is for. You can use token. Make sure the output text are only 128 characters.'),
'#default_value' => $settings['description'],
);
$form['tokens'] = array(
'#theme' => 'token_tree',
'#token_types' => array('commerce-order'),
'#global_types' => TRUE,
'#click_insert' => TRUE,
);
$form['log_status'] = array(
'#type' => 'checkbox',
'#title' => t('Log Transaction status'),
'#description' => t('Log the postback and return url status.'),
'#default_value' => $settings['log_status'],
);
return $form;
}
/**
* Payment method callback: Submit form.
*
* Adds a message to the submission form if enabled in the payment method.
*/
function commerce_dragonpay_submit_form($payment_method, $pane_values, $checkout_pane, $order) {
$form = array();
if (!empty($payment_method['settings']['show_payment_instructions'])) {
$form['Dragonpay_information'] = array(
'#markup' => '<span class="commerce-dragonpay--info">' . t('(Continue with checkout to complete payment via Dragonpay.)') . '</span>',
);
}
return $form;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function commerce_dragonpay_form_commerce_checkout_form_alter(&$form, &$form_state) {
// If this checkout form contains the payment method radios...
if (!empty($form['commerce_payment']['payment_method']['#options'])) {
// Loop over its options array looking for a Dragonpay option.
foreach ($form['commerce_payment']['payment_method']['#options'] as $key => &$value) {
list($method_id, $rule_name) = explode('|', $key);
// If we find Dragonpay ...
if ($method_id == 'dragonpay') {
// Prepare the replacement radio button text with icons.
$value = t('DragonPay - pay directly with your bank account or with over-the-counter using DragonPay secured payment portal.');
$form['buttons']['continue']['#validate'][] = 'commerce_dragonpay_form_commerce_checkout_form_validate';
break;
}
}
}
}
/**
* Validate currency.
*/
function commerce_dragonpay_form_commerce_checkout_form_validate(&$form, &$form_state) {
$order_wrapper = entity_metadata_wrapper('commerce_order', $form_state['order']);
$currency_code = $order_wrapper->commerce_order_total->currency_code->value();
$commerce_dragonpay_currencies = commerce_dragonpay_currencies();
if (!in_array($currency_code, $commerce_dragonpay_currencies)) {
form_set_error('', t('Sorry, your order currency is not supported by DragonPay.'));
}
}
/**
* Payment method callback: Redirect form.
*
* A wrapper around the module's general use function for building a form.
*/
function commerce_dragonpay_redirect_form($form, &$form_state, $order, $payment_method) {
// Return an error if the enabling action's settings haven't been configured.
if (empty($payment_method['settings']['merchantid'])) {
drupal_set_message(t('Dragonpay is not configured for use. No Dragonpay Merchant ID has been specified.'), 'error');
return array();
}
$settings = array(
// Return to the previous page when payment is canceled.
'cancel_return' => url('checkout/' . $order->order_id . '/payment/back/' . $order->data['payment_redirect_key'], array('absolute' => TRUE)),
// Return to the payment redirect page for processing successful payments.
'return' => url('checkout/' . $order->order_id . '/payment/return/' . $order->data['payment_redirect_key'], array('absolute' => TRUE)),
// Specify the current payment method instance ID in the notify_url.
'payment_method' => $payment_method['instance_id'],
// Include the application indicator.
'bn' => $payment_method['buttonsource'],
);
return commerce_dragonpay_order_form($form, $form_state, $order, $payment_method['settings'] + $settings);
}
/**
* Builds a Dragonpay form from an order object.
*
* @param object $order
* The fully loaded order being paid for.
* @param array $settings
* An array of settings used to build out the form, including:
* - server: which server to use, either sandbox or live
* - merchantid: the Dragonpay account ID
* - secretkey: the Dragonpay account password
* - currency_code: the Dragonpay currency code to use for this payment.
*
* @return array
* A renderable form array.
*/
function commerce_dragonpay_order_form($form, &$form_state, $order, array $settings) {
// Load the commerce order.
$wrapper = entity_metadata_wrapper('commerce_order', $order);
$currency_code = $settings['currency_code'];
$order_currency_code = $wrapper->commerce_order_total->currency_code->value();
// Convert the order currency to the selected currency on the settings. Dragonpay recommends to use PHP.
$amount = commerce_currency_convert($wrapper->commerce_order_total->amount->value(), $order_currency_code, $currency_code);
// Ensure a default value for the payment_method setting.
$settings += array('payment_method' => '');
// Build the data array that will be translated into hidden form values.
$data = array(
// Varchar(20) A unique code assigned to Merchant.
'merchantid' => $settings['merchantid'],
// Varchar(40) A unique id identifying this specific transaction.
'txnid' => commerce_dragonpay_generate_txnid($order),
// Numeric(12,2) The amount to get from the end-user.
'amount' => commerce_dragonpay_currency_amount_to_decimal($amount, $currency_code),
// Char(3) The currency of the amount.
'ccy' => $currency_code,
// Varchar(128) A brief description of what the payment is for.
'description' => token_replace($settings['description'], array('commerce-order' => $order)),
// Varchar(40) Email address of the customer.
'email' => $order->mail,
);
// Allow modules to alter parameters of the API request.
drupal_alter('commerce_dragonpay_order_form_data', $data, $order);
// Generate the digest key and add it to the data array.
$digest = commerce_dragonpay_generate_digest_key($data, $settings['secretkey']);
$data += array('digest' => $digest);
$server_url = commerce_dragonpay_server_url($settings['server']);
// Add the redirect path.
$form['#action'] = url($server_url, array('query' => $data, 'absolute' => TRUE));
foreach ($data as $name => $value) {
if (!empty($value)) {
$form[$name] = array('#type' => 'hidden', '#value' => $value);
}
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Proceed to Dragonpay'),
);
return $form;
}
/**
* Returns the URL to the specified Dragonpay server.
*
* @param string $server
* Either sandbox or live indicating which server to get the URL for.
*
* @return string
* The URL to use to submit requests to the Dragonpay server.
*/
function commerce_dragonpay_server_url($server) {
switch ($server) {
case 'sandbox':
return 'http://test.Dragonpay.ph/Pay.aspx';
case 'live':
return 'https://gw.Dragonpay.ph/Pay.aspx';
}
}
/**
* Returns the Order ID from generated txnid.
*
* @param string $txnid
* The generated transaction ID.
*
* @return int
* The extracted Order ID from txnid
*/
function commerce_dragonpay_extract_orderid_from_txnid($txnid) {
// Get the Numbers from the string.
preg_match_all('/([U]\d*)([O]\d*)([D]\d*)/', $txnid, $matches);
// Get the Order ID, expecting the first array to be the Order ID.
$order_id = str_replace('O', '', $matches[2][0]);
// Allow other module to extract the orderid if they generate their own.
drupal_alter('commerce_dragonpay_extract_orderid_from_txnid', $order_id, $txnid);
return $order_id;
}
/**
* Returns a generate txnid required by Dragonpay.
*
* @param object $order
* The commerce order object.
*
* @return string
* The generated txnid
*/
function commerce_dragonpay_generate_txnid($order) {
$settings = commerce_dragonpay_default_settings();
$txnid = token_replace($settings['txnid'], array('commerce-order' => $order));
// Allow other module to generate their own txnid.
drupal_alter('commerce_dragonpay_generate_txnid', $txnid, $order);
return $txnid;
}
/**
* Returns a sha1 string generated from the data and password.
*
* @param array $params
* The data array containing the following data: Merchant ID, Transaction ID,
* - Amount in decimal, Currency code, Simple description, and Email Address.
* @param string $passwd
* The secret code or password.
*
* @return string
* The generated sha1 string
*/
function commerce_dragonpay_generate_digest_key(array $params, $passwd) {
// If request came from callback. Only use required array;
if (isset($params['status']) && isset($params['refno'])) {
$retain = array_flip(array('txnid', 'refno', 'status', 'message'));
$params = array_intersect_key($params, $retain);
}
// Add the password into the data.
$params += array('passwd' => $passwd);
// Combine the array values.
$string = implode(':', $params);
// Return a sha1 string.
return sha1($string);
}
/**
* Returns an array of all possible currency codes allowed for Dragonpay.
*
* @param string $method_id
* The ID of the payment method whose currencies should be returned.
*
* @return array
* An associative array of currency codes with keys and values being the
* currency codes accepted by the specified Dragonpay payment method.
*/
function commerce_dragonpay_currencies($method_id = 'default') {
switch ($method_id) {
case 'default':
return drupal_map_assoc(array('PHP', 'USD'));
}
}
/**
* Converts a price amount to a decimal value based on the currency.
*
* Always return with a two decimal value.
*
* @param int $amount
* The price amount to convert to a decimal value.
* @param string $currency_code
* The currency code of the price whose decimals value will be used to divide
* - by the proper divisor when converting the amount.
*
* @return decimal
* The decimal amount in two decimal places.
*/
function commerce_dragonpay_currency_amount_to_decimal($amount, $currency_code) {
$val = commerce_currency_amount_to_decimal($amount, $currency_code);
return sprintf('%0.2f', $val);
}
/**
* Returns an array of Dragonpay status codes.
*
* @return array
* An array status codes of status code from DragonPay.
*/
function _commerce_dragonpay_status_codes() {
return array(
'S' => 'Success',
'F' => 'Failure',
'P' => 'Pending',
'U' => 'Unknown',
'R' => 'Refund',
'K' => 'Chargeback',
'V' => 'Void',
'A' => 'Authorized',
);
}
/**
* Returns an array of DragonPay error codes.
*
* @return array
* An array or error codes from DragonPay.
*/
function _commerce_dragonpay_error_codes() {
return array(
'000' => 'Success',
'101' => t('Invalid payment gateway id'),
'102' => t('Incorrect secret key'),
'103' => t('Invalid reference number'),
'104' => t('Unauthorized access'),
'105' => t('Invalid token'),
'106' => t('Currency not supported'),
'107' => t('Transaction cancelled'),
'108' => t('Insufficient funds'),
'109' => t('Transaction limit exceeded'),
'110' => t('Error in operation'),
'111' => t('Invalid parameters'),
'201' => t('Invalid Merchant Id'),
'202' => t('Invalid Merchant Password'),
);
}
/**
* Extract error code description.
*
* @param string $message
* The error code message sent by dragonpay.
*
* @return array
* Extracted error code and description
*/
function _commerce_dragonpay_extract_error_code($message) {
$array = explode('] ', $message);
if (count($array) > 1) {
$status['id'] = filter_var($array[0], FILTER_SANITIZE_NUMBER_INT);
$status['info'] = $array[1];
}
else {
$status['info'] = $array[0];
}
return $status;
}