protected function AcceptJs::doCreatePaymentMethod in Commerce Authorize.Net 8
Creates the payment method on the gateway.
This handles customer and payment profile creation, along with logic to handle Authorize.net's duplicate profile detection.
@link https://developer.authorize.net/api/reference/features/customer_profiles...
@todo Rename to customer profile @todo Make a method for just creating payment profile on existing profile.
Parameters
\Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method: The payment method.
array $payment_details: The gateway-specific payment details.
Return value
array The payment method information returned by the gateway. Notable keys:
- token: The remote ID.
Credit card specific keys:
- card_type: The card type.
- last4: The last 4 digits of the credit card number.
- expiration_month: The expiration month.
- expiration_year: The expiration year.
1 call to AcceptJs::doCreatePaymentMethod()
- AcceptJs::createPaymentMethod in src/
Plugin/ Commerce/ PaymentGateway/ AcceptJs.php - @todo Needs kernel test
File
- src/
Plugin/ Commerce/ PaymentGateway/ AcceptJs.php, line 607
Class
- AcceptJs
- Provides the Accept.js payment gateway.
Namespace
Drupal\commerce_authnet\Plugin\Commerce\PaymentGatewayCode
protected function doCreatePaymentMethod(PaymentMethodInterface $payment_method, array $payment_details) {
$owner = $payment_method
->getOwner();
$customer_profile_id = NULL;
$customer_data = [];
if ($owner && !$owner
->isAnonymous()) {
$customer_profile_id = $this
->getRemoteCustomerId($owner);
if (empty($customer_profile_id)) {
$customer_profile_id = $this
->getPaymentMethodCustomerId($payment_method);
}
$customer_data['email'] = $owner
->getEmail();
}
if ($customer_profile_id) {
$payment_profile = $this
->buildCustomerPaymentProfile($payment_method, $payment_details, $customer_profile_id);
$request = new CreateCustomerPaymentProfileRequest($this->authnetConfiguration, $this->httpClient);
$request
->setCustomerProfileId($customer_profile_id);
$request
->setPaymentProfile($payment_profile);
$response = $request
->execute();
if ($response
->getResultCode() != 'Ok') {
$this
->logResponse($response);
$error = $response
->getMessages()[0];
switch ($error
->getCode()) {
case 'E00039':
if (!isset($response->customerPaymentProfileId)) {
throw new InvalidResponseException('Duplicate payment profile ID, however could not get existing ID.');
}
break;
case 'E00040':
// The customer record ID is invalid, remove it.
// @note this should only happen in development scenarios.
$this
->setRemoteCustomerId($owner, NULL);
$owner
->save();
throw new InvalidResponseException('The customer record could not be found');
default:
throw new InvalidResponseException($error
->getText());
}
}
$payment_profile_id = $response->customerPaymentProfileId;
$validation_direct_response = explode(',', $response->validationDirectResponse);
}
else {
$request = new CreateCustomerProfileRequest($this->authnetConfiguration, $this->httpClient);
if ($owner
->isAuthenticated()) {
$profile = new Profile([
// @todo how to allow altering.
'merchantCustomerId' => $owner
->id(),
'email' => $owner
->getEmail(),
]);
}
else {
$profile = new Profile([
// @todo how to allow altering.
'merchantCustomerId' => $owner
->id() . '_' . $this->time
->getRequestTime(),
'email' => $payment_details['customer_email'],
]);
}
$request
->setProfile($profile);
// Due to their being a possible duplicate record for the customer
// profile, we cannot attach the payment profile in the initial request.
// If we did, it would invalidate the token generated by Accept.js and
// not allow us to reconcile duplicate payment methods.
//
// So we do not attach a payment profile and run two requests, and make
// sure no validation mode is executed.
$request
->setValidationMode(NULL);
$response = $request
->execute();
if ($response
->getResultCode() == 'Ok') {
$customer_profile_id = $response->customerProfileId;
}
else {
// Handle duplicate.
if ($response
->getMessages()[0]
->getCode() == 'E00039') {
$result = array_filter(explode(' ', $response
->getMessages()[0]
->getText()), 'is_numeric');
$customer_profile_id = reset($result);
}
else {
$this
->logResponse($response);
throw new InvalidResponseException("Unable to create customer profile.");
}
}
$payment_profile = $this
->buildCustomerPaymentProfile($payment_method, $payment_details, $customer_profile_id);
$request = new CreateCustomerPaymentProfileRequest($this->authnetConfiguration, $this->httpClient);
$request
->setCustomerProfileId($customer_profile_id);
$request
->setPaymentProfile($payment_profile);
$response = $request
->execute();
if ($response
->getResultCode() != 'Ok') {
// If the error is not due to duplicates, log and error.
if ($response
->getMessages()[0]
->getCode() != 'E00039') {
$this
->logResponse($response);
throw new InvalidResponseException("Unable to create payment profile for existing customer");
}
}
$payment_profile_id = $response->customerPaymentProfileId;
$validation_direct_response = explode(',', $response->validationDirectResponse);
if ($owner
->isAuthenticated()) {
$this
->setRemoteCustomerId($owner, $customer_profile_id);
$owner
->save();
}
}
// The result in validationDirectResponse does not properly escape any
// delimiter characters in the response data. So if the address, name, or
// email have "," the key for the card type is offset.
//
// We know the card type is after the XXXX#### masked card number.
$card_type_key = 51;
foreach ($validation_direct_response as $key => $value) {
if (!empty($value) && strpos($value, 'XXXX') !== FALSE) {
$card_type_key = $key + 1;
}
}
$remote_id = $owner
->isAuthenticated() ? $payment_profile_id : $customer_profile_id . '|' . $payment_profile_id;
return [
'remote_id' => $remote_id,
'card_type' => $validation_direct_response[$card_type_key],
'last4' => $payment_details['last4'],
'expiration_month' => $payment_details['expiration_month'],
'expiration_year' => $payment_details['expiration_year'],
];
}