initial version

This commit is contained in:
gasteve 2012-10-01 15:28:57 -04:00
parent 87cafac76c
commit c7b64edf70
9 changed files with 597 additions and 3 deletions

View File

@ -1,4 +1,43 @@
bitpayMagento
=============
©2011 BIT-PAY LLC.
Permission is hereby granted to any person obtaining a copy of this software
and associated documentation for use and/or modification in association with
the bitpay.com service.
Magento payment plugin for Bitpay.com
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.
Bitcoin payment module using the bitpay.com service.
Installation
------------
Copy these files into your Magento directory.
Configuration
-------------
1. Create an API key at bitpay.com by clicking My Account > API Access Keys > Add New API Key.
2. In Admin panel under "System > Configuration > Sales > Payment Methods > Bitcoins":
a. Verify that the module is enabled.
b. Enter your API key
c. Select a transaction speed. The high speed will send a confirmation as soon as a transaction is received in the bitcoin network (usually a few seconds). A medium speed setting will typically take 10 minutes. The low speed setting usually takes around 1 hour. See the bitpay.com merchant documentation for a full description of the transaction speed settings.
d. Verify that the currencies option includes your store's currencies. If it doesn't, check bitpay.com to see if they support your desired currency. If so, you may simply add the currency to the list using this setting. If not, you will not be able to use that currency.
Usage
-----
When a shopping chooses the Bitcoin payment method, they will be presented with an order summary as the next step (prices are shown in whatever currency they've selected for shopping). Upon receiving their order, the system takes the shopper to a bitpay.com invoice where the user is presented with bitcoin payment instructions. Once payment is received, a link is presented to the shopper that will take them to their "My Account" page.
In your Admin control panel, you can see the invoices associated with each order made with Bitcoins. The invoice will tell you whether payment has been received.
Note: This extension does not provide a means of automatically pulling a current BTC exchange rate for presenting BTC prices to shoppers.
Change Log
----------
Version 1
- Initial version, tested against Magento 1.6.0.0
Version 2
- Now supports API keys instead of SSL files. Tested against 1.7.0.2.

View File

@ -0,0 +1,163 @@
<?php
/**
* Our test CC module adapter
*/
class Bitpay_Bitcoins_Model_PaymentMethod extends Mage_Payment_Model_Method_Abstract
{
/**
* unique internal payment method identifier
*
* @var string [a-z0-9_]
*/
protected $_code = 'Bitcoins';
/**
* Here are examples of flags that will determine functionality availability
* of this module to be used by frontend and backend.
*
* @see all flags and their defaults in Mage_Payment_Model_Method_Abstract
*
* It is possible to have a custom dynamic logic by overloading
* public function can* for each flag respectively
*/
/**
* Is this payment method a gateway (online auth/charge) ?
*/
protected $_isGateway = true;
/**
* Can authorize online?
*/
protected $_canAuthorize = true;
/**
* Can capture funds online?
*/
protected $_canCapture = false;
/**
* Can capture partial amounts online?
*/
protected $_canCapturePartial = false;
/**
* Can refund online?
*/
protected $_canRefund = false;
/**
* Can void transactions online?
*/
protected $_canVoid = false;
/**
* Can use this payment method in administration panel?
*/
protected $_canUseInternal = false;
/**
* Can show this payment method as an option on checkout payment page?
*/
protected $_canUseCheckout = true;
/**
* Is this payment method suitable for multi-shipping checkout?
*/
protected $_canUseForMultishipping = true;
/**
* Can save credit card information for future processing?
*/
protected $_canSaveCc = false;
function canUseForCurrency($currencyCode)
{
$currencies = Mage::getStoreConfig('payment/Bitcoins/currencies');
$currencies = array_map('trim', explode(',', $currencies));
return array_search($currencyCode, $currencies) !== false;
}
function debuglog($contents)
{
$file = 'lib/bitpay/log.txt';
file_put_contents($file, date('m-d H:i:s').": \n", FILE_APPEND);
if (is_array($contents))
foreach($contents as $k => $v)
file_put_contents($file, $k.': '.$v."\n", FILE_APPEND);
else
file_put_contents($file, $contents."\n", FILE_APPEND);
}
public function canUseCheckout()
{
$secret = Mage::getStoreConfig('payment/Bitcoins/api_key');
if (!$secret or !strlen($secret))
{
Mage::log('Bitpay/Bitcoins: API key not entered');
return false;
}
$speed = Mage::getStoreConfig('payment/Bitcoins/speed');
if (!$speed or !strlen($speed))
{
Mage::log('Bitpay/Bitcoins: Transaction Speed invalid');
return false;
}
return $this->_canUseCheckout;
}
public function authorize(Varien_Object $payment, $amount)
{
include 'lib/bitpay/bp_lib.php';
$apiKey = Mage::getStoreConfig('payment/Bitcoins/api_key');
$speed = Mage::getStoreConfig('payment/Bitcoins/speed');
$order = $payment->getOrder();
$orderId = $order->getIncrementId();
$options = array(
//'physical' => ,
'currency' => $order->getBaseCurrencyCode(),
'buyerName' => $order->getCustomerFirstname().' '.$order->getCustomerLastname(),
'fullNotifications' => 'true',
'notificationURL' => Mage::getUrl('bitpay_callback'),
'redirectURL' => Mage::getUrl('customer/account'),
'transactionSpeed' => $speed,
'apiKey' => $apiKey,
);
$this->debuglog('creating bitpay invoice');
$this->debuglog($options);
$invoice = bpCreateInvoice($orderId, $amount, $orderId, $options);
$this->debuglog($invoice);
if (array_key_exists('error', $invoice))
{
Mage::log('Error creating bitpay invoice');
$this->debuglog('error creating bitpay invoice');
$this->debuglog($invoice['error']);
Mage::throwException("Error creating bit-pay invoice. Please try again or use another payment option.");
}
else
{
$this->debuglog('created bitpay invoice, setting redirect '.$invoice['url']);
$invoiceId = Mage::getModel('sales/order_invoice_api')->create($orderId, array());
Mage::getSingleton('customer/session')->setRedirectUrl($invoice['url']);
}
return $this;
}
public function getOrderPlaceRedirectUrl()
{
$url = Mage::getSingleton('customer/session')->getRedirectUrl();
$this->debuglog('returning redirect '.$url);
return $url;
}
}
?>

View File

@ -0,0 +1,24 @@
<?
class Bitpay_Bitcoins_Model_Source_Speed
{
public function toOptionArray()
{
return array(
array(
'value' => 'low',
'label' => 'Low',
),
array(
'value' => 'medium',
'label' => 'Medium',
),
array(
'value' => 'high',
'label' => 'High',
));
}
}
?>

View File

@ -0,0 +1,49 @@
<?
class Bitpay_Bitcoins_IndexController extends Mage_Core_Controller_Front_Action {
function debuglog($contents)
{
$file = 'lib/bitpay/log.txt';
file_put_contents($file, date('m-d H:i').": \n", FILE_APPEND);
if (is_array($contents))
foreach($contents as $k => $v)
file_put_contents($file, $k.': '.$v."\n", FILE_APPEND);
else
file_put_contents($file, $contents."\n", FILE_APPEND);
}
public function indexAction() {
require 'lib/bitpay/bp_lib.php';
$apiKey = Mage::getStoreConfig('payment/Bitcoins/api_key');
$response = bpVerifyNotification($apiKey);
if (is_string($response))
$this->debuglog("bitpay callback error: $response");
else {
$orderId = $response['posData'];
switch($response['status'])
{
case 'confirmed':
$order = Mage::getModel('sales/order')->loadByIncrementId($orderId);
$invoices = $order->getInvoiceCollection();
foreach($invoices as $i)
$i->pay()
->save();
$order->setState($order::STATE_PROCESSING)
->save();
break;
// (bit-pay.com does not send expired notifications as of this release)
case 'expired':
// could set invoices to canceled
break;
}
}
}
}
?>

View File

@ -0,0 +1,89 @@
<?xml version="1.0"?>
<config>
<frontend>
<routers>
<bitpay_callback>
<use>standard</use>
<args>
<module>Bitpay_Bitcoins</module>
<frontName>bitpay_callback</frontName>
</args>
</bitpay_callback>
</routers>
</frontend>
<modules>
<Bitpay_Bitcoins>
<!-- declare module's version information for database updates -->
<version>0.1.0</version>
</Bitpay_Bitcoins>
</modules>
<global>
<!-- IMPORTANT: if you use your own namespace (i.e. Bitpay) you also have to declare blocks group for new module. See topic: http://www.magentocommerce.com/boards/viewthread/22416/#t102732 -->
<blocks>
<Bitcoins>
<class>Bitpay_Bitcoins_Block</class>
</Bitcoins>
</blocks>
<!-- declare model group for new module -->
<models>
<!-- model group alias to be used in Mage::getModel('Bitcoins/...') -->
<Bitcoins>
<!-- base class name for the model group -->
<class>Bitpay_Bitcoins_Model</class>
</Bitcoins>
</models>
<!-- declare resource setup for new module -->
<resources>
<!-- resource identifier -->
<Bitcoins_setup>
<!-- specify that this resource is a setup resource and used for upgrades -->
<setup>
<!-- which module to look for install/upgrade files in -->
<module>Bitpay_Bitcoins</module>
</setup>
<!-- specify database connection for this resource -->
<connection>
<!-- do not create new connection, use predefined core setup connection -->
<use>core_setup</use>
</connection>
</Bitcoins_setup>
<Bitcoins_write>
<connection>
<use>core_write</use>
</connection>
</Bitcoins_write>
<Bitcoins_read>
<connection>
<use>core_read</use>
</connection>
</Bitcoins_read>
</resources>
</global>
<!-- declare default configuration values for this module -->
<default>
<!-- 'payment' configuration section (tab) -->
<payment>
<!-- 'Bitcoins' configuration group (fieldset) -->
<Bitcoins>
<!-- by default this payment method is active -->
<active>1</active>
<!-- model to handle logic for this payment method -->
<model>Bitcoins/paymentMethod</model>
<!-- default title for payment checkout page and order view page -->
<title>Bitcoins</title>
<speed>low</speed>
<currencies>BTC, USD, EUR, GBP, AUD, BGN, BRL, CAD, CHF, CNY, CZK, DKK, HKD, HRK, HUF, IDR, ILS, INR, JPY, KRW, LTL, LVL, MXN, MYR, NOK, NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, TRY, ZAR</currencies>
<payment_action>authorize</payment_action>
</Bitcoins>
</payment>
</default>
</config>

View File

@ -0,0 +1,65 @@
<?xml version="1.0"?>
<config>
<sections>
<payment>
<groups>
<Bitcoins translate="label" module="paygate">
<label>Bitcoins</label>
<sort_order>670</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<fields>
<active translate="label">
<label>Enabled</label>
<frontend_type>select</frontend_type>
<source_model>adminhtml/system_config_source_yesno</source_model>
<sort_order>1</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</active>
<title translate="label">
<label>Title</label>
<frontend_type>text</frontend_type>
<sort_order>1</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</title>
<api_key translate="label">
<label>API key</label>
<frontend_type>text</frontend_type>
<sort_order>2</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</api_key>
<speed translate="label">
<label>Transaction Speed</label>
<frontend_type>select</frontend_type>
<source_model>bitpay_bitcoins_model_source_speed</source_model>
<sort_order>5</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</speed>
<currencies translate="label">
<label>Currencies accepted by bit-pay.com</label>
<frontend_type>text</frontend_type>
<sort_order>6</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</currencies>
</fields>
</Bitcoins>
</groups>
</payment>
</sections>
</config>

View File

@ -0,0 +1,11 @@
<config>
<modules>
<Bitpay_Bitcoins>
<active>true</active>
<codePool>community</codePool>
<depends>
<Mage_Payment />
</depends>
</Bitpay_Bitcoins>
</modules>
</config>

126
lib/bitpay/bp_lib.php Normal file
View File

@ -0,0 +1,126 @@
<?php
require_once 'bp_options.php';
function bpCurl($url, $apiKey, $post = false) {
global $bpOptions;
$curl = curl_init($url);
$length = 0;
if ($post)
{
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
$length = strlen($post);
}
$uname = base64_encode($apiKey);
$header = array(
'Content-Type: application/json',
"Content-Length: $length",
"Authorization: Basic $uname",
);
curl_setopt($curl, CURLOPT_PORT, 443);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ) ;
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); // verify certificate
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); // check existence of CN and verify that it matches hostname
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_FORBID_REUSE, 1);
curl_setopt($curl, CURLOPT_FRESH_CONNECT, 1);
$responseString = curl_exec($curl);
if($responseString == false) {
$response = curl_error($curl);
} else {
$response = json_decode($responseString, true);
}
curl_close($curl);
return $response;
}
// $orderId: Used to display an orderID to the buyer. In the account summary view, this value is used to
// identify a ledger entry if present.
//
// $price: by default, $price is expressed in the currency you set in bp_options.php. The currency can be
// changed in $options.
//
// $posData: this field is included in status updates or requests to get an invoice. It is intended to be used by
// the merchant to uniquely identify an order associated with an invoice in their system. Aside from that, Bit-Pay does
// not use the data in this field. The data in this field can be anything that is meaningful to the merchant.
//
// $options keys can include any of:
// ('itemDesc', 'itemCode', 'notificationEmail', 'notificationURL', 'redirectURL', 'apiKey'
// 'currency', 'physical', 'fullNotifications', 'transactionSpeed', 'buyerName',
// 'buyerAddress1', 'buyerAddress2', 'buyerCity', 'buyerState', 'buyerZip', 'buyerEmail', 'buyerPhone')
// If a given option is not provided here, the value of that option will default to what is found in bp_options.php
// (see api documentation for information on these options).
function bpCreateInvoice($orderId, $price, $posData, $options = array()) {
global $bpOptions;
$options = array_merge($bpOptions, $options); // $options override any options found in bp_options.php
$options['posData'] = '{"posData": "' . $posData . '"';
if ($bpOptions['verifyPos']) // if desired, a hash of the POS data is included to verify source in the callback
$options['posData'].= ', "hash": "' . crypt($posData, $options['apiKey']).'"';
$options['posData'].= '}';
$options['orderID'] = $orderId;
$options['price'] = $price;
$postOptions = array('orderID', 'itemDesc', 'itemCode', 'notificationEmail', 'notificationURL', 'redirectURL',
'posData', 'price', 'currency', 'physical', 'fullNotifications', 'transactionSpeed', 'buyerName',
'buyerAddress1', 'buyerAddress2', 'buyerCity', 'buyerState', 'buyerZip', 'buyerEmail', 'buyerPhone');
foreach($postOptions as $o)
if (array_key_exists($o, $options))
$post[$o] = $options[$o];
$post = json_encode($post);
$response = bpCurl('https://bitpay.com/api/invoice/', $options['apiKey'], $post);
return $response;
}
// Call from your notification handler to convert $_POST data to an object containing invoice data
function bpVerifyNotification($apiKey = false) {
global $bpOptions;
if (!$apiKey)
$apiKey = $bpOptions['apiKey'];
$post = file_get_contents("php://input");
if (!$post)
return 'No post data';
$json = json_decode($post, true);
if (is_string($json))
return $json; // error
if (!array_key_exists('posData', $json))
return 'no posData';
$posData = json_decode($json['posData'], true);
if($bpOptions['verifyPos'] and $posData['hash'] != crypt($posData['posData'], $apiKey))
return 'authentication failed (bad hash)';
$json['posData'] = $posData['posData'];
return $json;
}
// $options can include ('apiKey')
function bpGetInvoice($invoiceId, $apiKey=false) {
global $bpOptions;
if (!$apiKey)
$apiKey = $bpOptions['apiKey'];
$response = bpCurl('https://bitpay.com/api/invoice/'.$invoiceId, $apiKey);
if (is_string($response))
return $response; // error
$response['posData'] = json_decode($response['posData'], true);
return $response;
}
?>

28
lib/bitpay/bp_options.php Normal file
View File

@ -0,0 +1,28 @@
<?php
global $bpOptions;
// do not edit this file
// all values are set via config settings or upon use
$bpOptions['SSLcert'] = '';
$bpOptions['SSLkey'] = '';
$bpOptions['secret'] = '';
$bpOptions['notificationEmail'] = '';
$bpOptions['notificationURL'] = '';
$bpOptions['redirectURL'] = '';
$bpOptions['currency'] = '';
$bpOptions['physical'] = 'true';
$bpOptions['fullNotifications'] = 'true';
$bpOptions['transactionSpeed'] = 'low';
?>