View source
<?php
namespace Drupal\uc_paypal\Plugin\Ubercart\PaymentMethod;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\uc_credit\CreditCardPaymentMethodBase;
use Drupal\uc_order\OrderInterface;
use GuzzleHttp\Exception\TransferException;
class PayPalWebsitePaymentsPro extends CreditCardPaymentMethodBase {
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
'wps_email' => '',
'wpp_server' => 'https://api-3t.sandbox.paypal.com/nvp',
'api' => [
'api_username' => '',
'api_password' => '',
'api_signature' => '',
],
];
}
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['wps_email'] = [
'#type' => 'email',
'#title' => $this
->t('PayPal e-mail address'),
'#description' => $this
->t('The e-mail address you use for the PayPal account you want to receive payments.'),
'#default_value' => $this->configuration['wps_email'],
];
$form['wpp_server'] = [
'#type' => 'select',
'#title' => $this
->t('API server'),
'#description' => $this
->t('Sign up for and use a Sandbox account for testing.'),
'#options' => [
'https://api-3t.sandbox.paypal.com/nvp' => $this
->t('Sandbox'),
'https://api-3t.paypal.com/nvp' => $this
->t('Live'),
],
'#default_value' => $this->configuration['wpp_server'],
];
$form['api'] = [
'#type' => 'details',
'#title' => $this
->t('API credentials'),
'#description' => $this
->t('@link for information on obtaining credentials. You need to acquire an API Signature. If you have already requested API credentials, you can review your settings under the API Access section of your PayPal profile.', [
'@link' => Link::fromTextAndUrl($this
->t('Click here'), Url::fromUri('https://developer.paypal.com/docs/classic/api/apiCredentials/'))
->toString(),
]),
'#open' => TRUE,
];
$form['api']['api_username'] = [
'#type' => 'textfield',
'#title' => $this
->t('API username'),
'#default_value' => $this->configuration['api']['api_username'],
];
$form['api']['api_password'] = [
'#type' => 'textfield',
'#title' => $this
->t('API password'),
'#default_value' => $this->configuration['api']['api_password'],
];
$form['api']['api_signature'] = [
'#type' => 'textfield',
'#title' => $this
->t('Signature'),
'#default_value' => $this->configuration['api']['api_signature'],
];
return $form;
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
$this->configuration['wps_email'] = trim($form_state
->getValue('wps_email'));
$this->configuration['wpp_server'] = $form_state
->getValue('wpp_server');
$this->configuration['api']['api_username'] = $form_state
->getValue([
'settings',
'api',
'api_username',
]);
$this->configuration['api']['api_password'] = $form_state
->getValue([
'settings',
'api',
'api_password',
]);
$this->configuration['api']['api_signature'] = $form_state
->getValue([
'settings',
'api',
'api_signature',
]);
}
protected function chargeCard(OrderInterface $order, $amount, $txn_type, $reference = NULL) {
if ($txn_type == UC_CREDIT_PRIOR_AUTH_CAPTURE) {
$nvp_request = [
'METHOD' => 'DoCapture',
'AUTHORIZATIONID' => $reference,
'AMT' => uc_currency_format($amount, FALSE, FALSE, '.'),
'CURRENCYCODE' => $order
->getCurrency(),
'COMPLETETYPE' => 'Complete',
];
}
else {
if (intval($order->payment_details['cc_exp_month']) < 10) {
$expdate = '0' . $order->payment_details['cc_exp_month'] . $order->payment_details['cc_exp_year'];
}
else {
$expdate = $order->payment_details['cc_exp_month'] . $order->payment_details['cc_exp_year'];
}
$cc_type = NULL;
if (isset($order->payment_details['cc_type'])) {
switch (strtolower($order->payment_details['cc_type'])) {
case 'amex':
case 'american express':
$cc_type = 'Amex';
break;
case 'visa':
$cc_type = 'Visa';
break;
case 'mastercard':
case 'master card':
$cc_type = 'MasterCard';
break;
case 'discover':
$cc_type = 'Discover';
break;
}
}
if (is_null($cc_type)) {
$cc_type = $this
->cardType($order->payment_details['cc_number']);
if ($cc_type === FALSE) {
$this
->messenger()
->addError($this
->t('The credit card type did not pass validation.'));
\Drupal::logger('uc_paypal')
->error('Could not figure out cc type: @number / @type', [
'@number' => $order->payment_details['cc_number'],
'@type' => $order->payment_details['cc_type'],
]);
return [
'success' => FALSE,
];
}
}
$ip_address = ltrim(\Drupal::request()
->getClientIp(), '::ffff:');
$address = $order
->getAddress('billing');
$nvp_request = [
'METHOD' => 'DoDirectPayment',
'PAYMENTACTION' => $txn_type == UC_CREDIT_AUTH_ONLY ? 'Authorization' : 'Sale',
'IPADDRESS' => $ip_address,
'AMT' => uc_currency_format($amount, FALSE, FALSE, '.'),
'CREDITCARDTYPE' => $cc_type,
'ACCT' => $order->payment_details['cc_number'],
'EXPDATE' => $expdate,
'CVV2' => $order->payment_details['cc_cvv'],
'FIRSTNAME' => substr($address
->getFirstName(), 0, 25),
'LASTNAME' => substr($address
->getLastName(), 0, 25),
'STREET' => substr($address
->getStreet1(), 0, 100),
'STREET2' => substr($address
->getStreet2(), 0, 100),
'CITY' => substr($address
->getCity(), 0, 40),
'STATE' => $address
->getZone(),
'ZIP' => $address
->getPostalCode(),
'COUNTRYCODE' => $address
->getCountry(),
'CURRENCYCODE' => $order
->getCurrency(),
'DESC' => $this
->t('Order @order_id at @store', [
'@order_id' => $order
->id(),
'@store' => uc_store_name(),
]),
'INVNUM' => $order
->id() . '-' . REQUEST_TIME,
'BUTTONSOURCE' => 'Ubercart_ShoppingCart_DP_US',
'NOTIFYURL' => Url::fromRoute('uc_paypal.ipn', [], [
'absolute' => TRUE,
])
->toString(),
'EMAIL' => substr($order
->getEmail(), 0, 127),
'PHONENUM' => substr($address
->getPhone(), 0, 20),
];
if ($order
->isShippable()) {
$address = $order
->getAddress('delivery');
$nvp_request += [
'SHIPTONAME' => substr($address
->getFirstName() . ' ' . $address
->getLastName(), 0, 25),
'SHIPTOSTREET' => substr($address
->getStreet1(), 0, 100),
'SHIPTOSTREET2' => substr($address
->getStreet2(), 0, 100),
'SHIPTOCITY' => substr($address
->getCity(), 0, 40),
'SHIPTOSTATE' => $address
->getZone(),
'SHIPTOZIP' => $address
->getPostalCode(),
'SHIPTOCOUNTRYCODE' => $address
->getCountry(),
];
}
}
$nvp_response = $this
->sendNvpRequest($nvp_request);
$types = uc_credit_transaction_types();
switch ($nvp_response['ACK']) {
case 'SuccessWithWarning':
\Drupal::logger('uc_paypal')
->warning('<b>@type succeeded with a warning.</b>@paypal_message', [
'@paypal_message' => $this
->buildErrorMessages($nvp_response),
'@type' => $types[$txn_type],
'link' => $order
->toLink($this
->t('view order'))
->toString(),
]);
case 'Success':
$message = $this
->t('<b>@type</b><br /><b>Success: </b>@amount @currency', [
'@type' => $types[$txn_type],
'@amount' => uc_currency_format($nvp_response['AMT'], FALSE),
'@currency' => $nvp_response['CURRENCYCODE'],
]);
if ($txn_type != UC_CREDIT_PRIOR_AUTH_CAPTURE) {
$message .= '<br />' . $this
->t('<b>Address:</b> @avscode', [
'@avscode' => $this
->avscodeMessage($nvp_response['AVSCODE']),
]);
$message .= '<br />' . $this
->t('<b>CVV2:</b> @cvvmatch', [
'@cvvmatch' => $this
->cvvmatchMessage($nvp_response['CVV2MATCH']),
]);
}
$result = [
'success' => TRUE,
'comment' => $this
->t('PayPal transaction ID: @transactionid', [
'@transactionid' => $nvp_response['TRANSACTIONID'],
]),
'message' => $message,
'data' => [
'module' => 'uc_paypal',
'txn_id' => (string) SafeMarkup::checkPlain($nvp_response['TRANSACTIONID']),
],
'uid' => \Drupal::currentUser()
->id(),
];
if ($txn_type == UC_CREDIT_AUTH_ONLY) {
uc_credit_log_authorization($order
->id(), $nvp_response['TRANSACTIONID'], $nvp_response['AMT']);
}
elseif ($txn_type == UC_CREDIT_PRIOR_AUTH_CAPTURE) {
uc_credit_log_prior_auth_capture($order
->id(), $reference);
}
$this->database
->insert('uc_payment_paypal_ipn')
->fields([
'order_id' => $order
->id(),
'txn_id' => $nvp_response['TRANSACTIONID'],
'txn_type' => 'web_accept',
'mc_gross' => $amount,
'status' => 'Completed',
'payer_email' => $order
->getEmail(),
'received' => REQUEST_TIME,
])
->execute();
break;
case 'FailureWithWarning':
case 'Failure':
$message = $this
->t('<b>@type failed.</b>', [
'@type' => $types[$txn_type],
]) . $this
->buildErrorMessages($nvp_response);
$result = [
'success' => FALSE,
'message' => $message,
'uid' => \Drupal::currentUser()
->id(),
];
break;
default:
$message = $this
->t('Unexpected acknowledgement status: @status', [
'@status' => $nvp_response['ACK'],
]);
$result = [
'success' => NULL,
'message' => $message,
'uid' => \Drupal::currentUser()
->id(),
];
break;
}
uc_order_comment_save($order
->id(), \Drupal::currentUser()
->id(), $message, 'admin');
if (in_array($txn_type, [
UC_CREDIT_AUTH_ONLY,
])) {
$result['log_payment'] = FALSE;
}
return $result;
}
public function sendNvpRequest($params) {
$host = $this->configuration['wpp_server'];
$params += [
'USER' => $this->configuration['api']['api_username'],
'PWD' => $this->configuration['api']['api_password'],
'SIGNATURE' => $this->configuration['api']['api_signature'],
'VERSION' => '3.0',
];
try {
$response = \Drupal::httpClient()
->request('POST', $host, [
'form_params' => $params,
]);
parse_str($response
->getBody(), $output);
return $output;
} catch (TransferException $e) {
\Drupal::logger('uc_paypal')
->error('NVP API request failed with HTTP error %error.', [
'%error' => $e
->getMessage(),
]);
}
}
protected function buildErrorMessages($nvp_response) {
$code = 0;
$message = '';
while (array_key_exists('L_SEVERITYCODE' . $code, $nvp_response)) {
$message .= '<br /><b>' . SafeMarkup::checkPlain($nvp_response['L_SEVERITYCODE' . $code]) . ':</b> ' . SafeMarkup::checkPlain($nvp_response['L_ERRORCODE' . $code]) . ': ' . SafeMarkup::checkPlain($nvp_response['L_LONGMESSAGE' . $code]);
$code++;
}
return $message;
}
protected function cardType($cc_number) {
switch (substr(strval($cc_number), 0, 1)) {
case '3':
return 'Amex';
case '4':
return 'Visa';
case '5':
return 'MasterCard';
case '6':
return 'Discover';
}
return FALSE;
}
protected function avscodeMessage($code) {
if (is_numeric($code)) {
switch ($code) {
case '0':
return $this
->t('All the address information matched.');
case '1':
return $this
->t('None of the address information matched; transaction declined.');
case '2':
return $this
->t('Part of the address information matched.');
case '3':
return $this
->t('The merchant did not provide AVS information. Not processed.');
case '4':
return $this
->t('Address not checked, or acquirer had no response. Service not available.');
default:
return $this
->t('No AVS response was obtained.');
}
}
switch ($code) {
case 'A':
case 'B':
return $this
->t('Address matched; postal code did not');
case 'C':
case 'N':
return $this
->t('Nothing matched; transaction declined');
case 'D':
case 'F':
case 'X':
case 'Y':
return $this
->t('Address and postal code matched');
case 'E':
return $this
->t('Not allowed for MOTO transactions; transaction declined');
case 'G':
return $this
->t('Global unavailable');
case 'I':
return $this
->t('International unavailable');
case 'P':
case 'W':
case 'Z':
return $this
->t('Postal code matched; address did not');
case 'R':
return $this
->t('Retry for validation');
case 'S':
return $this
->t('Service not supported');
case 'U':
return $this
->t('Unavailable');
case 'Null':
return $this
->t('No AVS response was obtained.');
default:
return $this
->t('An unknown error occurred.');
}
}
protected function cvvmatchMessage($code) {
if (is_numeric($code)) {
switch ($code) {
case '0':
return $this
->t('Matched');
case '1':
return $this
->t('No match');
case '2':
return $this
->t('The merchant has not implemented CVV2 code handling.');
case '3':
return $this
->t('Merchant has indicated that CVV2 is not present on card.');
case '4':
return $this
->t('Service not available');
default:
return $this
->t('Unkown error');
}
}
switch ($code) {
case 'M':
return $this
->t('Match');
case 'N':
return $this
->t('No match');
case 'P':
return $this
->t('Not processed');
case 'S':
return $this
->t('Service not supported');
case 'U':
return $this
->t('Service not available');
case 'X':
return $this
->t('No response');
default:
return $this
->t('Not checked');
}
}
}