Skip to content

Instantly share code, notes, and snippets.

@CodeAngry
Created June 30, 2012 20:26
Show Gist options
  • Save CodeAngry/3025366 to your computer and use it in GitHub Desktop.
Save CodeAngry/3025366 to your computer and use it in GitHub Desktop.
PaypalAPI DoDirectPayment Sale PHP Implementation :: Untested, wrote at anger! :)
<?php
// ------------------------------------------------------------------------------------------ //
/**
* LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @category Payments
* @package CA\APIs\Paypal
* @author CodeAngry <codeangry at google's mail dot com>
* @copyright Claude "CodeAngry" Adrian
* @license http://www.opensource.org/licenses/mit-license.html
* @version 0.0.0
* @link http://codeangry.com/
*/
// ------------------------------------------------------------------------------------------ //
/**
* __toArray() enforcer.
*/
interface PaypalArray {
/**
* Easy function to cast Properties to arbitrary array.
*
* @return array
*/
public function __toArray();
}
/**
* Holds Person information.
*/
class PaypalPerson implements PaypalArray {
/**
* Self-explanatory.
*
* @var string
*/
protected $_FirstName = null;
/**
* Self-explanatory.
*
* @var string
*/
protected $_LastName = null;
/**
* Self-explanatory.
*
* @var string
*/
protected $_StreetAddress = null;
/**
* Self-explanatory.
*
* @var string
*/
protected $_City = null;
/**
* Self-explanatory.
*
* @var string
*/
protected $_State = null;
/**
* Self-explanatory.
*
* @var string
*/
protected $_CountryCode = null;
/**
* Self-explanatory.
*
* @var number
*/
protected $_ZipCode = null;
/**
* Easy __construct()-or that sets up all properties in one strike.
*
* @param string $FirstName
* @param string $LastName
* @param string $StreetAddress
* @param string $City
* @param string $State
* @param string $CountryCode
* @param number $ZipCode
* @return PaypalPerson
*/
public function __construct($FirstName, $LastName, $StreetAddress, $City, $State, $CountryCode, $ZipCode){
// Walk string Arguments and enfore validity
foreach(compact('FirstName', 'LastName', 'StreetAddress', 'City', 'State') as $Name => $Value){
if(!is_string($Value) or !strlen($this->{"_{$Name}"} = trim($Value))){
throw new \InvalidArgumentException("{$Name} must be a non-empty trimmed string.");
}
}
// CountryCode is 2-letter uppercased string
if(!is_string($CountryCode) or !preg_match('~^[A-Z]{2}$~', $this->_CountryCode = trim(strtoupper($CardType)))){
throw new \InvalidArgumentException('$CountryCode must be a non-empty trimmed string.');
}
// Zip is numeric
if(!is_numeric($ZipCode) or (($this->_ZipCode = intval($ZipCode)) < 1)){
throw new \InvalidArgumentException('$ZipCode must be a positive number.');
}
}
/**
* Export Properties.
*
* @return array
*/
public function __toArray(){
return array(
'FIRSTNAME' => $this->_FirstName,
'LASTNAME' => $this->_LastName,
'STREET' => $this->_StreetAddress,
'CITY' => $this->_City,
'STATE' => $this->_State,
'COUNTRYCODE' => $this->_CountryCode,
'ZIP' => $this->_ZipCode,
);
}
}
/**
* Holds CreditCard information.
*/
class PaypalCreditCard implements PaypalArray {
/**
* Self-explanatory.
*
* @var string
*/
protected $_CardType = null;
/**
* Self-explanatory.
*
* @var number
*/
protected $_CardNumber = null;
/**
* Self-explanatory.
*
* @var number
*/
protected $_Expiration = null;
/**
* Self-explanatory.
*
* @var number
*/
protected $_CVV2 = null;
/**
* Easy __construct()-or that sets up all properties in one strike.
*
* @param string $CardType
* @param number $CardNumber
* @param number $ExpirationMonth
* @param number $ExpirationYear
* @param number $CVV2
* @return PaypalPerson
*/
public function __construct($CardType, $CardNumber, $ExpirationMonth, $ExpirationYear, $CVV2){
if(!is_string($CardType) or !strlen($this->_CardType = trim($CardType))){
throw new \InvalidArgumentException('$CardType must be a non-empty trimmed string.');
}
if(empty($CardNumber) or !is_numeric($CardNumber) or ($CardNumber < 1)){
throw new \InvalidArgumentException('$CardNumber must be a positive number.');
}
// Change this at will :)
if(!is_numeric($CVV2) or (($this->_CVV2 = intval($CVV2)) < 100) or ($CVV2 > 10000)){
throw new \InvalidArgumentException('$CVV2 must be a number of 3-4 digits.');
}
if(!is_numeric($ExpirationMonth) or (($ExpirationMonth = intval($ExpirationMonth)) < 1) or ($ExpirationMonth > 12)){
throw new \InvalidArgumentException('$ExpirationMonth must be between 1 and 12.');
}
// We check the year again so positive will do here
if(!is_numeric($ExpirationYear) or (($ExpirationYear = intval($ExpirationYear)) < 1)){
throw new \InvalidArgumentException('$ExpirationYear must be between 1 and 12.');
}
// Check future Expiration date
if((($ExpirationYear * 12) + $ExpirationMonth) < ((idate('Y') * 12) + idate('m'))){
throw new \InvalidArgumentException('$ExpirationYear and $ExpirationMonth cannot be in the past.');
}
// Format the Expiration date
$this->_Expiration = sprintf('%02d%04d', $ExpirationMonth, $ExpirationYear);
}
/**
* Export Properties.
*
* @return array
*/
public function __toArray(){
return array(
'CREDITCARDTYPE' => $this->_CardType,
'ACCT' => $this->_CardNumber,
'EXPDATE' => $this->_Expiration,
'CVV2' => $this->_CVV2,
);
}
}
/**
* Holds Payment information.
*/
class PaypalPayment implements PaypalArray {
/**
* Self-explanatory.
*
* @var float
*/
protected $_Amount = null;
/**
* Self-explanatory.
*
* @var string
*/
protected $_Currency = null;
/**
* Self-explanatory.
*
* @var string
*/
protected $_Description = null;
/**
* Easy __construct()-or that sets up all properties in one strike.
*
* @param float $Amount
* @param string $Currency
* @param string/null $Description
* @return PaypalPayment
*/
public function __construct($Amount, $Currency, $Description = null){
// Float Amount, positive so we don't pay the buyer :)
if(!is_numeric($Amount) or (($this->Amount = floatval($Amount)) <= 0)){
throw new \InvalidArgumentException('$Amount must be a positive float.');
}
// Currency is 3-letter uppercased string
if(!is_string($Currency) or !preg_match('~^[A-Z]{3}$~', $this->_Currency = trim(strtoupper($Currency)))){
throw new \InvalidArgumentException('$Currency must be a non-empty trimmed string.');
}
// Zip is numeric
empty($Description) and ($Description = null);
if(!is_null($Description) and (!is_string($Description) or !strlen($Description = trim($Description)))){
throw new \InvalidArgumentException('$Description must be NULL or a non-empty trimmed string.');
}
$this->_Description = $Description;
}
/**
* Export Properties.
*
* @return array
*/
public function __toArray(){
return array(
'AMT' => sprintf('%0.2f', $this->_Amount),
'CURRENCYCODE' => $this->_Currency,
'DESC' => $this->_Description
);
}
}
/**
* Allows easy PaypalAPI calls.
* It's directly __invoke()-able.
*/
class PaypalAPI {
/**
* Self-explanatory.
*
* @var string
*/
private $_Username = null;
/**
* Self-explanatory.
*
* @var string
*/
private $_Password = null;
/**
* Self-explanatory.
*
* @var string
*/
private $_Signature = null;
/**
* Self-explanatory.
*
* @var float
*/
private $_Version = null;
/**
* Self-explanatory.
*
* @var string
*/
private $_Endpoint = null;
/**
* Easy __construct()-or that sets up all properties in one strike.
*
* @param bool $Sandbox
* @param string $Username
* @param string $Password
* @param string $Signature
* @param float $Version
* @return PaypalDirectPayment
*/
public function __construct($Sandbox, $Username, $Password, $Signature, $Version = '85.0'){
if(!is_bool($Sandbox)){
throw new \InvalidArgumentException('$Sandbox needs to be BOOL, so do not make me guess.');
}
$this->_Endpoint = $Sandbox ? 'https://api-3t.sandbox.paypal.com/nvp' : 'https://api-3t.paypal.com/nvp';
if(!is_string($Username) or !strlen($this->_Username = trim($Username))){
throw new \InvalidArgumentException('$Username must be a non-empty trimmed string.');
}
if(!is_string($Password) or !strlen($this->_Password = trim($Password))){
throw new \InvalidArgumentException('$Password must be a non-empty trimmed string.');
}
if(!is_string($Signature) or !strlen($this->_Signature = trim($Signature))){
throw new \InvalidArgumentException('$Signature must be a non-empty trimmed string.');
}
$this->_Version = sprintf('%0.1f', floatval($Version)); // Force ##.# output to fix int usage
}
/**
* Does API calls. Method and Arguments.
* $Response will contain raw Response to our Request.
*
* @param string $Method
* @param array $Arguments
* @param reference $Response
* @return array/bool/null
*/
public function __invoke($Method, array $Arguments, &$Response = null){
$Response = null;
if(!is_string($Method) or !strlen($Method = trim($Method))){
throw new \InvalidArgumentException('$Sandbox must be a non-empty trimmed string.');
}
$Arguments = array_merge(array(
'METHOD' => $Method,
'USER' => $this->_Username,
'PWD' => $this->_Password,
'SIGNATURE' => $this->_Signature,
'VERSION' => sprintf('%0.1f', $this->_Version), // Just to make sure .0 decimal stays
), $Arguments);
// Really bad if this happens
if(!($cURL = curl_init($this->_Endpoint))){
return false;
}
curl_setopt($cURL, CURLOPT_VERBOSE, false);
curl_setopt($cURL, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($cURL, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($cURL, CURLOPT_TIMEOUT, 30);
curl_setopt($cURL, CURLOPT_URL, $this->_Endpoint); // Redundant, I know
curl_setopt($cURL, CURLOPT_RETURNTRANSFER, true);
curl_setopt($cURL, CURLOPT_HEADER, false);
curl_setopt($cURL, CURLOPT_PORT, true);
curl_setopt($cURL, CURLOPT_POSTFIELDS, $Arguments);
curl_setopt($cURL, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($cURL, CURLOPT_FORBID_REUSE, true);
curl_setopt($cURL, CURLOPT_FRESH_CONNECT, true);
curl_setopt($cURL, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($cURL, CURLOPT_TIMEOUT, 60);
curl_setopt($cURL, CURLOPT_HTTPHEADER, array(
'Connection: close',
'Expect: ',
));
$Response = curl_exec($cURL);
$Status = (int)curl_getinfo($cURL, CURLINFO_HTTP_CODE);
curl_close($cURL);
if($Status != 200){
return false; // Bad status
}
if(empty($Response) or !is_string($Response) or !strlen($Response = trim($Response))){
return null; // Empty data
}
parse_str($Response, $NVPs);
if(!isset($NVPs) or empty($NVPs) or !is_array($NVPs)){
return null; // Bad data
}
return $NVPs;
}
/**
* DoDirectPayment sale API Method.
*
* @param PaypalPayment $Payment
* @param PayPalCreditCard $CreditCard
* @param PaypalPerson $Person
* @return array
*/
public function DoDirectPayment(PaypalPayment $Payment, PayPalCreditCard $CreditCard, PaypalPerson $Person, &$Response = null) {
// Merge Arguments and Parameters :)
$Arguments = array_merge(array(
'PAYMENTACTION' => 'Sale',
'IPADDRESS' => $_SERVER['REMOTE_ADDR'],
), $Person->__toArray(), $CreditCard->__toArray(), $Payment->__toArray());
return $this('DoDirectPayment', $Arguments, $Response);
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment