<?php
namespace Braintree;
use InvalidArgumentException;
use DateTime;
use DateTimeZone;
/**
* Braintree Transparent Redirect Gateway module
* Static class providing methods to build Transparent Redirect urls
*
* @package Braintree
* @category Resources
*/
class TransparentRedirectGateway
{
private $_gateway;
private $_config;
public function __construct($gateway)
{
$this->_gateway = $gateway;
$this->_config = $gateway->config;
$this->_config->assertHasAccessTokenOrKeys();
}
/**
*
* @ignore
*/
private static $_transparentRedirectKeys = 'redirectUrl';
private static $_createCustomerSignature;
private static $_updateCustomerSignature;
private static $_transactionSignature;
private static $_createCreditCardSignature;
private static $_updateCreditCardSignature;
/**
* create signatures for different call types
* @ignore
*/
public static function init()
{
self::$_createCustomerSignature = [
self::$_transparentRedirectKeys,
['customer' => CustomerGateway::createSignature()],
];
self::$_updateCustomerSignature = [
self::$_transparentRedirectKeys,
'customerId',
['customer' => CustomerGateway::updateSignature()],
];
self::$_transactionSignature = [
self::$_transparentRedirectKeys,
['transaction' => TransactionGateway::createSignature()],
];
self::$_createCreditCardSignature = [
self::$_transparentRedirectKeys,
['creditCard' => CreditCardGateway::createSignature()],
];
self::$_updateCreditCardSignature = [
self::$_transparentRedirectKeys,
'paymentMethodToken',
['creditCard' => CreditCardGateway::updateSignature()],
];
}
public function confirm($queryString)
{
$params = TransparentRedirect::parseAndValidateQueryString(
$queryString
);
$confirmationKlasses = [
TransparentRedirect::CREATE_TRANSACTION => 'Braintree\TransactionGateway',
TransparentRedirect::CREATE_CUSTOMER => 'Braintree\CustomerGateway',
TransparentRedirect::UPDATE_CUSTOMER => 'Braintree\CustomerGateway',
TransparentRedirect::CREATE_PAYMENT_METHOD => 'Braintree\CreditCardGateway',
TransparentRedirect::UPDATE_PAYMENT_METHOD => 'Braintree\CreditCardGateway',
];
$confirmationGateway = new $confirmationKlasses[$params["kind"]]($this->_gateway);
return $confirmationGateway->_doCreate('/transparent_redirect_requests/' . $params['id'] . '/confirm', []);
}
/**
* returns the trData string for creating a credit card,
* @param array $params
* @return string
*/
public function createCreditCardData($params)
{
Util::verifyKeys(
self::$_createCreditCardSignature,
$params
);
$params["kind"] = TransparentRedirect::CREATE_PAYMENT_METHOD;
return $this->_data($params);
}
/**
* returns the trData string for creating a customer.
* @param array $params
* @return string
*/
public function createCustomerData($params)
{
Util::verifyKeys(
self::$_createCustomerSignature,
$params
);
$params["kind"] = TransparentRedirect::CREATE_CUSTOMER;
return $this->_data($params);
}
public function url()
{
return $this->_config->baseUrl() . $this->_config->merchantPath() . '/transparent_redirect_requests';
}
/**
* returns the trData string for creating a transaction
* @param array $params
* @return string
*/
public function transactionData($params)
{
Util::verifyKeys(
self::$_transactionSignature,
$params
);
$params["kind"] = TransparentRedirect::CREATE_TRANSACTION;
$transactionType = isset($params['transaction']['type']) ?
$params['transaction']['type'] :
null;
if ($transactionType != Transaction::SALE && $transactionType != Transaction::CREDIT) {
throw new InvalidArgumentException(
'expected transaction[type] of sale or credit, was: ' .
$transactionType
);
}
return $this->_data($params);
}
/**
* Returns the trData string for updating a credit card.
*
* The paymentMethodToken of the credit card to update is required.
*
* <code>
* $trData = TransparentRedirect::updateCreditCardData(array(
* 'redirectUrl' => 'http://example.com/redirect_here',
* 'paymentMethodToken' => 'token123',
* ));
* </code>
*
* @param array $params
* @return string
*/
public function updateCreditCardData($params)
{
Util::verifyKeys(
self::$_updateCreditCardSignature,
$params
);
if (!isset($params['paymentMethodToken'])) {
throw new InvalidArgumentException(
'expected params to contain paymentMethodToken.'
);
}
$params["kind"] = TransparentRedirect::UPDATE_PAYMENT_METHOD;
return $this->_data($params);
}
/**
* Returns the trData string for updating a customer.
*
* The customerId of the customer to update is required.
*
* <code>
* $trData = TransparentRedirect::updateCustomerData(array(
* 'redirectUrl' => 'http://example.com/redirect_here',
* 'customerId' => 'customer123',
* ));
* </code>
*
* @param array $params
* @return string
*/
public function updateCustomerData($params)
{
Util::verifyKeys(
self::$_updateCustomerSignature,
$params
);
if (!isset($params['customerId'])) {
throw new InvalidArgumentException(
'expected params to contain customerId of customer to update'
);
}
$params["kind"] = TransparentRedirect::UPDATE_CUSTOMER;
return $this->_data($params);
}
public function parseAndValidateQueryString($queryString)
{
// parse the params into an array
parse_str($queryString, $params);
// remove the hash
$queryStringWithoutHash = null;
if (preg_match('/^(.*)&hash=[a-f0-9]+$/', $queryString, $match)) {
$queryStringWithoutHash = $match[1];
}
if($params['http_status'] != '200') {
$message = null;
if(array_key_exists('bt_message', $params)) {
$message = $params['bt_message'];
}
Util::throwStatusCodeException(isset($params['http_status']) ? $params['http_status'] : null, $message);
}
// recreate the hash and compare it
if ($this->_hash($queryStringWithoutHash) == $params['hash']) {
return $params;
} else {
throw new Exception\ForgedQueryString();
}
}
/**
*
* @ignore
*/
private function _data($params)
{
if (!isset($params['redirectUrl'])) {
throw new InvalidArgumentException(
'expected params to contain redirectUrl'
);
}
$params = $this->_underscoreKeys($params);
$now = new DateTime('now', new DateTimeZone('UTC'));
$trDataParams = array_merge($params,
[
'api_version' => Configuration::API_VERSION,
'public_key' => $this->_config->publicKey(),
'time' => $now->format('YmdHis'),
]
);
ksort($trDataParams);
$urlEncodedData = http_build_query($trDataParams);
$signatureService = new SignatureService(
$this->_config->privateKey(),
"Braintree\Digest::hexDigestSha1"
);
return $signatureService->sign($urlEncodedData);
}
private function _underscoreKeys($array)
{
foreach($array as $key=>$value)
{
$newKey = Util::camelCaseToDelimiter($key, '_');
unset($array[$key]);
if (is_array($value))
{
$array[$newKey] = $this->_underscoreKeys($value);
}
else
{
$array[$newKey] = $value;
}
}
return $array;
}
/**
* @ignore
*/
private function _hash($string)
{
return Digest::hexDigestSha1($this->_config->privateKey(), $string);
}
}
TransparentRedirectGateway::init();
class_alias('Braintree\TransparentRedirectGateway', 'Braintree_TransparentRedirectGateway');