Commit c89ab30f authored by David Weston's avatar David Weston

Initial commit

Yeah, things are probably broken and there are no examples. But yeah,
I'll get around to making examples for it once I know it actually works
with SecureTrading's new API.
parents
php-stpp
--------
Currently in development. Not properly tested at the moment, so do not expect it to work right now.
Licenced under MIT, I'll put in the *actual* licence file in here some time.
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* The addressable object includes functionality for addresses and such, as
* quite a lot of functionality is shared between billing and customers, for
* example.
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
abstract class STPPAddressable extends STPPObject
{
/**
* Store all of the options this object holds in here.
*/
protected $options = array();
/**
* Set the e-mail address of this addressable object.
*/
public function setEmail($email)
{
$this->options["email"] = $email;
return $this;
}
/**
* Set the premise of this addressable object.
*/
public function setPremise($premise)
{
$this->options["premise"] = $premise;
return $this;
}
/**
* Set the street of this addressable object.
*/
public function setStreet($street)
{
$this->options["street"] = $street;
return $this;
}
/**
* Set the town of this addressable object.
*/
public function setTown($town)
{
$this->options["town"] = $town;
return $this;
}
/**
* Set the county of this addressable object.
*/
public function setCounty($county)
{
$this->options["county"] = $county;
return $this;
}
/**
* Set the post/zip-code of this addressable object.
*/
public function setPostcode($postcode)
{
$this->options["postcode"] = $postcode;
return $this;
}
/**
* Set the first name of the person referenced in this addressable object.
*/
public function setNamePrefix($prefix)
{
if(!isset($this->options["name"]))
$this->options["name"] = array();
$this->options["name"]["prefix"] = $prefix;
return $this;
}
/**
* Set the first name of the person referenced in this addressable object.
*/
public function setFirstName($prefix)
{
if(!isset($this->options["name"]))
$this->options["name"] = array();
$this->options["name"]["first"] = $prefix;
return $this;
}
/**
* Set the first name of the person referenced in this addressable object.
*/
public function setMiddleName($middle)
{
if(!isset($this->options["name"]))
$this->options["name"] = array();
$this->options["name"]["middle"] = $middle;
return $this;
}
/**
* Set the first name of the person referenced in this addressable object.
*/
public function setLastName($last)
{
if(!isset($this->options["name"]))
$this->options["name"] = array();
$this->options["name"]["last"] = $last;
return $this;
}
/**
* Set the first name of the person referenced in this addressable object.
*/
public function setNameSuffix($suffix)
{
if(!isset($this->options["name"]))
$this->options["name"] = array();
$this->options["name"]["suffix"] = $suffix;
return $this;
}
/**
* Set the telephone associated with the person referenced in this addressable
* object, and if not already defined, set the telephone type to be 'home'.
*/
public function setTelephone($phone)
{
if(!isset($this->options["telephone"]))
$this->options["telephone"] = array();
if(empty($this->options["telephone"]["type"]))
$this->options["telephone"]["type"] = "H";
$this->options["telephone"]["number"] = $phone;
return $this;
}
/**
* Set the type/location of the telephone given. Valid values are 'H', 'M', or 'W',
* for home/mobile/work respectively.
*/
public function setTelephoneType($type)
{
if(!isset($this->options["telephone"]))
$this->options["telephone"] = array();
$this->options["telephone"]["type"] = $type;
return $this;
}
/**
* Deals with compiling the name node(s).
*/
protected function compileName($element)
{
if(empty($this->options["name"]))
return false;
ksort($this->options["name"]);
$node = $element->addChild("name");
foreach($this->options["name"] as $option => $value)
$node->addChild($option, $value);
return true;
}
/**
* Deals with compiling the telephone node.
*/
protected function compileTelephone($element)
{
if(empty($this->options["telephone"]))
return false;
$element->addChild("telephone", $this->options["telephone"]["number"])->addAttribute("type", $this->options["telephone"]["type"]);
return true;
}
}
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* This is the object that deals with billing, such as who owns
* the card and the like. It's recommended that everything stuck into
* this class matches the name registered on the card. Addresses
* are also key.
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
class STPPBilling extends STPPAddressable
{
/**
* Store all of the options this object holds in here.
*/
protected $options = array();
/**
* Set the amount that this transaction is for. If not already defined,
* the currency shall be in GBP. Decimal units are accepted here, and will
* be automatically translated into a integer pence value.
*/
public function setAmount($amount)
{
if(!isset($this->options["amount"]))
$this->options["amount"] = array();
if(empty($this->options["amount"]["currencycode"]))
$this->options["amount"]["currencycode"] = "GBP";
$this->options["amount"]["value"] = (integer) ($amount * 100);
return $this;
}
/**
* Set the currency that this transaction shall be performed in.
*/
public function setCurrency($currency)
{
if(!isset($this->options["amount"]))
$this->options["amount"] = array();
$this->options["amount"]["currencycode"] = $currency;
return $this;
}
/**
* Set the payment card type - something like VISA or MASTERCARD. Values will
* always be translated into uppercase.
*/
public function setPaymentType($type)
{
if(!isset($this->options["payment"]))
$this->options["payment"] = array();
$this->options["payment"]["type"] = strtoupper($type);
return $this;
}
/**
* Set the credit or debit card number.
*/
public function setPaymentCardNumber($number)
{
if(!isset($this->options["payment"]))
$this->options["payment"] = array();
$set = array
(
" ",
"-",
);
$this->options["payment"]["pan"] = str_replace($set, "", $number);
return $this;
}
/**
* Set the registered expiry date of the payment method that is being used.
*
* Quite a few formats are accepted:
* - MM/YYYY -> default format
* - [ MM, YY or YYYY ]
* - { month: MM, year: YY or YYYY }
*/
public function setPaymentExpiryDate($expirydate)
{
if(!isset($this->options["payment"]))
$this->options["payment"] = array();
if(is_array($expirydate))
{
$month = null;
$year = null;
if(isset($expirydate["month"]))
{
$month = $expirydate["month"];
$year = $expirydate["year"];
}
else
{
$month = $expirydate[0];
$year = $expirydate[1];
}
$month = str_pad($month, 2, "0", STR_PAD_LEFT);
if($year < 100)
$year = 2000 + $year;
$expirydate = $month."/".$year;
}
elseif(is_numeric($expirydate) || is_int($expirydate))
{
$expirydate = date("m/Y", $expirydate);
}
$this->options["payment"]["expirydate"] = (string) $expirydate;
return $this;
}
/**
* Set the security code of the card that is being used in this transaction.
*/
public function setPaymentSecurityCode($code)
{
if(!isset($this->options["payment"]))
$this->options["payment"] = array();
$this->options["payment"]["securitycode"] = $code;
return $this;
}
/**
* Compiles the amount values.
*/
protected function compileAmount($element)
{
if(empty($this->options["amount"]))
return false;
$element->addChild("amount", $this->options["amount"]["value"])->addAttribute("currencycode", $this->options["amount"]["currencycode"]);
return true;
}
/**
* Compiles the payment information.
*/
protected function compilePayment($element)
{
if(empty($this->options["payment"]))
return false;
ksort($this->options["payment"]);
$node = $element->addChild("payment");
if(isset($this->options["payment"]["type"]))
$node->addAttribute("type", $this->options["payment"]["type"]);
unset($this->options["payment"]["type"]);
foreach($this->options["payment"] as $option => $value)
$node->addChild($option, $value);
return true;
}
}
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* This is the object that represents a customer. You can either point
* instructions to this class directly, or you can use the STAPI class
* to delegate them.
*
* Customer information does not constitute billing information, so there's
* no real need to fill in anything like addresses and such - however there
* /is/ a recommendation from ST to do so.
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
class STPPCustomer extends STPPAddressable
{
/**
* Store all of the options this object holds in here.
*/
protected $options = array();
/**
* Set the e-mail address of the customer.
*/
public function setEmail($email)
{
$this->options["email"] = $email;
return $this;
}
/**
* Set the IP that has been used to set up the request.
*/
public function setIP($address)
{
$this->options["forwardedip"] = $address;
return $this;
}
/**
* Set the IP address that has been proxied across during this request.
*/
public function setForwardedIP($address)
{
$this->options["forwardedip"] = $address;
return $this;
}
/**
* Set the user agent's Accept header - for use only with 3D secure.
*/
public function setAcceptHeader($accept)
{
$this->options["accept"] = $accept;
return $this;
}
/**
* Set the user agent - for use only with 3D secure.
*/
public function setUserAgent($accept)
{
$this->options["useragent"] = $accept;
return $this;
}
}
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* This is the object that represents a merchant. You can either point
* instructions to this class directly, or you can use the STAPI class
* to delegate them.
*
* Merchant information is usually not required when using STPP, however
* it is a required feature for 3D-Secure, which most if not all
* transactions should be performed using (if available).
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
class STPPMerchant extends STPPObject
{
/**
* Set the name of the merchant.
*/
public function setName($name)
{
$this->options["name"] = $name;
return $this;
}
/**
* Set the e-mail address of the merchant.
*/
public function setEmail($email)
{
$this->options["email"] = $email;
return $this;
}
/**
* Set the merchant-facing order reference of this order.
*/
public function setOrderReference($order)
{
$this->options["orderreference"] = $order;
return $this;
}
/**
* Set the terminating URL - for use in 3DSecure transactions.
*/
public function set3DSecureTermUrl($url)
{
$this->options["termurl"] = $url;
return $this;
}
}
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* This is the god object for all parts of the request, such as
* the merchant, operations and such.
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
abstract class STPPObject
{
/**
* Store all of the options this object holds in here.
*/
protected $options = array();
/**
* Deals with compiling an XML node with which we can use in
* fulfilling a request to the endpoint.
*/
public function compile($element)
{
if(!$this->options)
return false;
ksort($this->options);
foreach($this->options as $option => $value)
{
$method = "compile".$option;
if(method_exists($this, $method))
$this->$method($element);
else
$element->addChild(strtolower($option), $value);
}
return true;
}
/**
* Retrieves the options defined using the API.
*/
public final function getOptions()
{
return $this->options;
}
}
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* The operation object contains some other information that is needed
* to complete the request.
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
class STPPOperation extends STPPObject
{
/**
* Set the account type description of the call. Would normally
* default to ECOM, you wouldn't usually need to vary from this.
*/
public function setAccountTypeDescription($type)
{
$this->options["accounttypedescription"] = $type;
return $this;
}
/**
* Set the parent transaction reference.
*/
public function setParentTransactionReference($reference)
{
$this->options["parenttransactionreference"] = $reference;
return $this;
}
/**
* Set the site reference that is being used in this operation.
*/
public function setSiteReference($reference)
{
$this->options["sitereference"] = $reference;
return $this;
}
/**
* Set the 3DSecure MD parameter. Based on values sent back from ST.
*/
public function set3DSecureMD($value)
{
$this->options["md"] = $value;
return $this;
}
/**
* Set the 3DSecure PaReq (payer authentication request) value.
* Based on values sent back from ST.
*/
public function set3DSecurePaReq($value)
{
$this->options["pareq"] = $value;
return $this;
}
}
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* The STPPResponse object parses the response from the SecureTrading endpoint,
* and puts it into a nice easy to use output.
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
class STPPResponse
{
/**
* Store the XML response somewhere...
*/
protected $feed = null;
/**
* Called when the response has been constructed.
*/
public function __construct($response)
{
$this->feed = simplexml_load_string($response);
return true;
}
/**
* Retrieves the XML object that was sent back from SecureTrading.
*/
public function getResponse()
{
return $this->feed;
}
/**
* Check if the request that was sent was successful.
*/
public function isSuccessful()
{
if(!isset($this->feed->error->code))
return null;
return ((integer) $this->feed->error->code == 0);
}
/**
* Retrieves the transaction reference that applies to this
* transaction.
*/
public function getTransactionReference()
{
return (string) $this->feed->response->transactionreference;
}
/**
* If provided in the response, retrieves the security hints given in the response
* and parses them into easier to understand/evaluate types.
*
* (null) -> unable to verify
* (false) -> not valid
* (true) -> valid
*/
public function getSecurityRating()
{
if(!isset($this->feed->response->security))
return array();
$set = array();
foreach($this->feed->response->security as $node)
{
switch((integer) $node)
{
case 0:
case 1:
{
$set[$node->getName()] = null;
break;
}
case 2:
{
$set[$node->getName()] = true;
break;
}
case 4:
{
$set[$node->getName()] = false;
break;
}
}
}
return $set;
}
}
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* This is the object that represents updated settlement details.
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
class STPPSettlement extends STPPObject
{
/**
* Set the settlement due date. Please note that you cannot
* settle further than seven days after the transaction has
* been authorised.
*/
public function setDate($settledate)
{
if(is_array($settledate))
{
$day = null;
$month = null;
$year = null;
if(isset($settledate["day"]))
{
$day = $settledate["day"];
$month = $settledate["month"];
$year = $settledate["year"];
}
else
{
$day = $settledate[0];
$month = $settledate[1];
$year = $settledate[2];
}
$day = str_pad($day, 2, "0", STR_PAD_LEFT);
$month = str_pad($month, 2, "0", STR_PAD_LEFT);
if($year < 100)
$year = 2000 + $year;
$settledate = $month."/".$year;
}
elseif(is_numeric($settledate) || is_int($settledate))
{
$settledate = date("d/m/Y", $settledate);
}
$this->options["payment"]["settleduedate"] = (string) $settledate;
return $this;
}
/**
* Set the status of the settlement. You can find more information
* on this from the STPP documentation.
*/
public function setStatus($status)
{
$this->options["payment"]["settlestatus"] = (string) $settledate;
return $this;
}
}
\ No newline at end of file
<?php
/**
* PHP based wrapper for SecureTrading's new STPP protocol.
*
* This won't allow you to connect your system immediately
* to a setup, however, it'll allow you to with ease create
* and maintain a new contract with a SecureTrading node.
*
* @version: untested
* @author: David Weston <stpp@typefish.co.uk>
*/
/**
* Okay, so it's probably a good thing to include quite a lot of
* classes that we need for this object to work properly.
*/
require "objects/stppobject.php";
require "objects/stppaddressableobject.php";
require "objects/stppbilling.php";
require "objects/stppcustomer.php";
require "objects/stppmerchant.php";
require "objects/stppoperation.php";
require "objects/stppsettlement.php";
require "objects/stppresponse.php";
/**
* Actual STAPI object
*/
class STAPI
{
/**
* Details about the connection to our local endpoint.
*/
protected $alias = null;
protected $connection = null;
/**
* Stores each of the objects used in this request.
*/
protected $objects = array();
/**
* Called whenever we want to start talking with SecureTrading.
*/
public function __construct($address = "127.0.0.1", $port = 5000)
{
# $this->connect($address, $port);
}
/**
* A destructor, destructing things.
*/
public function __destruct()
{
# $this->disconnect();
}
/**
* Begin a connection to ST.
*/
protected function connect($address, $port)
{
$errno = null;
$errstr = null;
if($this->connection = @fsockopen($address, $port, $errno, $errstr))
return true;
return false;
}
/**
* Kills our connection to ST.
*/
protected function disconnect()
{
return fclose($this->connection);
}
/**
* Set the alias of the current request.
*/
public function setAlias($alias)
{
$this->alias = $alias;
return $this;
}
/**
* Sets the billing object - you can either separately supply an object
* that represents a billing, or you can use the simulated methods which
* will do pretty much the same thing.
*/
public function setBilling($billing)
{
$this->objects["billing"] = $billing;
return $this;
}
/**
* Retrieves the object that is used to represent the billing.
*/
public function getBilling()
{
if(!isset($this->objects["billing"]))
$this->resetBilling();
return $this->objects["billing"];
}
/**
* Clears the merchant to a blank state.
*/
public function resetBilling()
{
$this->objects["billing"] = new STPPBilling();
return $this;
}
/**
* Sets the customer object - you can either separately supply an object
* that represents a customer, or you can use the simulated methods which
* will do pretty much the same thing.
*/
public function setCustomer($customer)
{
$this->objects["customer"] = $customer;
return $this;
}
/**
* Retrieves the object that is used to represent the customer.
*/
public function getCustomer()
{
if(!isset($this->objects["customer"]))
$this->resetCustomer();
return $this->objects["customer"];
}
/**
* Clears the merchant to a blank state.
*/
public function resetCustomer()
{
$this->objects["customer"] = new STPPCustomer();
return $this;
}
/**
* Sets the merchant object - you can either separately supply an object
* that represents a merchant, or you can use the simulated methods which
* will do pretty much the same thing.
*/
public function setMerchant($merchant)
{
$this->objects["merchant"] = $merchant;
return $this;
}
/**
* Retrieves the object that is used to represent the merchant.
*/
public function getMerchant()
{
if(!isset($this->objects["merchant"]))
$this->resetMerchant();
return $this->objects["merchant"];
}
/**
* Clears the merchant to a blank state.
*/
public function resetMerchant()
{
$this->objects["merchant"] = new STPPMerchant();
return $this;
}
/**
* Sets the operation object - you can either separately supply an object
* that represents an operation, or you can use the simulated methods which
* will do pretty much the same thing.
*/
public function setOperation($operation)
{
$this->objects["operation"] = $operation;
return $this;
}
/**
* Retrieves the object that is used to represent the operation.
*/
public function getOperation()
{
if(!isset($this->objects["operation"]))
$this->resetOperation();
return $this->objects["operation"];
}
/**
* Clears the operation to a blank state.
*/
public function resetOperation()
{
$this->objects["operation"] = new STPPOperation();
return $this;
}
/**
* Sets the settlement object - you can either separately supply an object
* that represents a settlement, or you can use the simulated methods which
* will do pretty much the same thing.
*/
public function setSettlement($settlement)
{
$this->objects["settlement"] = $settlement;
return $this;
}
/**
* Retrieves the object that is used to represent the settlement.
*/
public function getSettlement()
{
if(!isset($this->objects["settlement"]))
$this->resetSettlement();
return $this->objects["settlement"];
}
/**
* Clears the settlement to a blank state.
*/
public function resetSettlement()
{
$this->objects["settlement"] = new STPtSettlement();
return $this;
}
/**
* Some fancy __call abuse - we'll manhandle the supplied function name, then
* try to figure out what component it needs to call, then call it. Simple!
*
* If the result call returns an instance of STPPObject (IE: something like billing)
* then /this/ class will be returned instead, to maintain the similiarities in
* calling conventions.
*
* Otherwise, the correct value will be returned unmolested.
*/
public function __call($method, $arguments)
{
$set = array();
preg_match_all("/((?:^|[A-Z])[a-z]+)/", $method, $set);
$set = $set[0];
$caller = "get".$set[1];
unset($set[1]);
$method = implode("", $set);
if(!method_exists($this, $caller))
return null;
$callback = array
(
$this->$caller(),
$method,
);
$result = call_user_func_array($callback, $arguments);
if($result instanceof STPPObject)
return $this;
return $result;
}
/**
* Used to push a request off to the SecureTrading endpoint.
*/
public function call($type)
{
$request = $this->compile($type)->asXML();
if(!fwrite($this->connection, $request, strlen($request)))
return false;
$response = "";
while(($chunk = fread($this->connection, 4096)) !== false)
$response .= $chunk;
return new STPPResponse($response);
}
/**
* Called when compiling all of the nodes required for this request to properly proceed.
* Nothing like inter-breeding SimpleXML and DOM!
*/
public function compile($method)
{
$envelope = new SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?><requestblock></requestblock>");
$envelope->addAttribute("version", "3.67");
$envelope->addChild("alias", $this->alias);
$method = strtoupper($method);
$request = $envelope->addChild("request");
$request->addAttribute("type", $method);
ksort($this->objects);
$document = dom_import_simplexml($request);
foreach($this->objects as $object => $node)
{
$element = new SimpleXMLElement("<".$object."></".$object.">");
if(!$node->compile($element))
continue;
$component = dom_import_simplexml($element);
$component = $document->ownerDocument->importNode($component, true);
$document->appendChild($component);
}
return $envelope;
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment