mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 02:02:41 +01:00
Add WePay as a one-time payment provider
Summary: Ref T2787. I //think// we could also use WePay as a recurring payment provider, but this is somewhat messy (OAuth + requires account) -- basically it's "add a WePay account" instead of "add a credit card". The WePay checkout workflow is a bit upsell-y but basically reasonable. I like that their API just has a `request($method, $params)` method instead of 30,000 lines of methods for each request type. I did hit one bug; I'll send a pull for that. Test Plan: Got as far as the charge callback in testing; the rest isn't implemented for any provider yet. Reviewers: btrahan, vrana, chad Reviewed By: btrahan CC: aran Maniphest Tasks: T2787 Differential Revision: https://secure.phabricator.com/D5982
This commit is contained in:
parent
fab9a138f3
commit
49ef13e876
15 changed files with 905 additions and 0 deletions
85
externals/wepay/README.md
vendored
Normal file
85
externals/wepay/README.md
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
WePay PHP SDK
|
||||
=============
|
||||
|
||||
WePay's API allows you to easily add payments into your application.
|
||||
|
||||
For full documentation, see [WePay's developer documentation](https://www.wepay.com/developer)
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
In addition to the samples below, we have included a very basic demo application in the `demoapp` directory. See its README file for additional information.
|
||||
|
||||
### Configuration ###
|
||||
|
||||
For all requests, you must initialize the SDK with your Client ID and Client Secret, into either Staging or Production mode. All API calls made against WePay's staging environment mirror production in functionality, but do not actually move money. This allows you to develop your application and test the checkout experience from the perspective of your users without spending any money on payments. Our [full documentation](https://www.wepay.com/developer) contains additional information on test account numbers you can use in addition to "magic" amounts you can use to trigger payment failures and reversals (helpful for testing IPNs).
|
||||
|
||||
**Note:** Staging and Production are two completely independent environments and share NO data with each other. This means that in order to use staging, you must register at [stage.wepay.com](https://stage.wepay.com/developer) and get a set of API keys for your Staging application, and must do the same on Production when you are ready to go live. API keys and access tokens granted on stage *can not* be used on Production, and vice-versa.
|
||||
|
||||
<?php
|
||||
require './wepay.php';
|
||||
WePay::useProduction('YOUR CLIENT ID', 'YOUR CLIENT SECRET'); // To initialize staging, use WePay::useStaging('ID','SECRET'); instead.
|
||||
|
||||
### Authentication ###
|
||||
|
||||
To obtain an access token for your user, you must redirect the user to WePay for authentication. WePay uses OAuth2 for authorization, which is detailed [in our documentation](https://www.wepay.com/developer/reference/oauth2). To generate the URI to which you must redirect your user, the SDK contains `WePay::getAuthorizationUri($scope, $redirect_uri)`. `$scope` should be an array of scope strings detailed in the documentation. To request full access (most useful for testing, since users may be weary of granting permission to your application if it wants to do too much), you pay pass in `WePay::getAllScopes()`. `$redirect_uri` must be a fully qualified URI where we will send the user after permission is granted (or not granted), and the domain must match your application settings.
|
||||
|
||||
If the user grants permission, he or she will be redirected to your `$redirect_uri` with `code=XXXX` appended to the query string. If permission is not granted, we will instead put `error=XXXX` in the query string. If `code` is present, the following will exchange it for an access token. Note that codes are only valid for several minutes, so you should do this immediately after the user is redirected back to your website or application.
|
||||
|
||||
<?php
|
||||
if (!empty($_GET['error'])) {
|
||||
// user did not grant permissions
|
||||
}
|
||||
elseif (empty($_GET['code'])) {
|
||||
// set $scope and $redirect_uri before doing this
|
||||
// this will send the user to WePay to authenticate
|
||||
$uri = WePay::getAuthorizationUri($scope, $redirect_uri);
|
||||
header("Location: $uri");
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
$info = WePay::getToken($_GET['code'], $redirect_uri);
|
||||
if ($info) {
|
||||
// YOUR ACCESS TOKEN IS HERE
|
||||
$access_token = $info->access_token;
|
||||
}
|
||||
else {
|
||||
// Unable to obtain access token
|
||||
}
|
||||
}
|
||||
|
||||
Full details on the access token response are [here](https://www.wepay.com/developer/reference/oauth2#token).
|
||||
|
||||
**Note:** If you only need access for yourself (e.g., for a personal storefront), the application settings page automatically creates an access token for you. Simply copy and paste it into your code rather than manually going through the authentication flow.
|
||||
|
||||
### Making API Calls ###
|
||||
|
||||
With the `$access_token` from above, get a new SDK object:
|
||||
|
||||
<?php
|
||||
$wepay = new WePay($access_token);
|
||||
|
||||
Then you can make a simple API call. This will list the user's accounts available to your application:
|
||||
|
||||
// (continued from above)
|
||||
try {
|
||||
$accounts = $wepay->request('account/find');
|
||||
foreach ($accounts as $account) {
|
||||
echo "<a href=\"$account->account_uri\">$account->name</a>: $account->description <br />";
|
||||
}
|
||||
}
|
||||
catch (WePayException $e) {
|
||||
// Something went wrong - normally you would log
|
||||
// this and give your user a more informative message
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
And that's it! For more detail on what API calls are available, their parameters and responses, and what permissions they require, please see [our documentation](https://www.wepay.com/developer/reference). For some more detailed examples, look in the `demoapp` directory and check the README. Dropping the entire directory in a web-accessible location and adding your API keys should allow you to be up and running in just a few seconds.
|
||||
|
||||
### SSL Certificate ###
|
||||
|
||||
If making an API call causes the following problem:
|
||||
|
||||
Uncaught exception 'Exception' with message 'cURL error while making API call to WePay: SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed'
|
||||
|
||||
You can read the solution here: https://support.wepay.com/entries/21095813-problem-with-ssl-certificate-verification
|
19
externals/wepay/demoapp/README
vendored
Normal file
19
externals/wepay/demoapp/README
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
After registering your application at wepay.com (or stage.wepay.com), you
|
||||
need to make two updates to this application:
|
||||
|
||||
1 - set your client_id and client_secret in _shared.php
|
||||
2 - set the redirect_uri in login.php
|
||||
|
||||
That should be enough to start making API calls against WePay's API. While
|
||||
this is by no means a production-ready example, it should provide you a
|
||||
couple ideas on how to get running.
|
||||
|
||||
It also defaults to requesting all possible scope fields in the
|
||||
authentication request. We suggest limiting the request to the minimum
|
||||
your application requires, which will maximize the chance the user
|
||||
grants permissions to your application. You can customize this in
|
||||
login.php.
|
||||
|
||||
If you have any questions, please contact the API team: api@wepay.com
|
||||
|
||||
- WePay
|
4
externals/wepay/demoapp/_shared.php
vendored
Normal file
4
externals/wepay/demoapp/_shared.php
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
require '../wepay.php';
|
||||
Wepay::useStaging('YOUR CLIENT ID', 'YOUR CLIENT SECRET');
|
||||
session_start();
|
20
externals/wepay/demoapp/accountlist.php
vendored
Normal file
20
externals/wepay/demoapp/accountlist.php
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
?>
|
||||
<h1>WePay Demo App: Account List</h1>
|
||||
<a href="index.php">Back</a>
|
||||
<br />
|
||||
|
||||
<?php
|
||||
try {
|
||||
$wepay = new WePay($_SESSION['wepay_access_token']);
|
||||
$accounts = $wepay->request('account/find');
|
||||
foreach ($accounts as $account) {
|
||||
echo "<a href=\"$account->account_uri\">$account->name</a>: $account->description <br />";
|
||||
}
|
||||
}
|
||||
catch (WePayException $e) {
|
||||
// Something went wrong - normally you would log
|
||||
// this and give your user a more informative message
|
||||
echo $e->getMessage();
|
||||
}
|
20
externals/wepay/demoapp/index.php
vendored
Normal file
20
externals/wepay/demoapp/index.php
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
?>
|
||||
|
||||
<h1>WePay Demo App</h1>
|
||||
<?php if (empty($_SESSION['wepay_access_token'])): ?>
|
||||
|
||||
<a href="login.php">Log in with WePay</a>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<a href="user.php">User info</a>
|
||||
<br />
|
||||
<a href="openaccount.php">Open new account</a>
|
||||
<br />
|
||||
<a href="accountlist.php">Account list</a>
|
||||
<br />
|
||||
<a href="logout.php">Log out</a>
|
||||
|
||||
<?php endif; ?>
|
41
externals/wepay/demoapp/login.php
vendored
Normal file
41
externals/wepay/demoapp/login.php
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
|
||||
// ** YOU MUST CHANGE THIS FOR THE SAMPLE APP TO WORK **
|
||||
$redirect_uri = 'http://YOUR SERVER NAME/login.php';
|
||||
$scope = WePay::getAllScopes();
|
||||
|
||||
// If we are already logged in, send the user home
|
||||
if (!empty($_SESSION['wepay_access_token'])) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// If the authentication dance returned an error, catch it to avoid a
|
||||
// redirect loop. This usually indicates some sort of application issue,
|
||||
// like a domain mismatch on your redirect_uri
|
||||
if (!empty($_GET['error'])) {
|
||||
echo 'Error during user authentication: ';
|
||||
echo htmlentities($_GET['error_description']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// If we don't have a code from being redirected back here,
|
||||
// send the user to WePay to grant permissions.
|
||||
if (empty($_GET['code'])) {
|
||||
$uri = WePay::getAuthorizationUri($scope, $redirect_uri);
|
||||
header("Location: $uri");
|
||||
}
|
||||
else {
|
||||
$info = WePay::getToken($_GET['code'], $redirect_uri);
|
||||
if ($info) {
|
||||
// Normally you'd integrate this into your existing auth system
|
||||
$_SESSION['wepay_access_token'] = $info->access_token;
|
||||
// If desired, you can also store $info->user_id somewhere
|
||||
header('Location: index.php');
|
||||
}
|
||||
else {
|
||||
// Unable to obtain access token
|
||||
echo 'Unable to obtain access token from WePay.';
|
||||
}
|
||||
}
|
6
externals/wepay/demoapp/logout.php
vendored
Normal file
6
externals/wepay/demoapp/logout.php
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
session_start();
|
||||
$_SESSION = array();
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
50
externals/wepay/demoapp/openaccount.php
vendored
Normal file
50
externals/wepay/demoapp/openaccount.php
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
?>
|
||||
<h1>WePay Demo App: Open Account</h1>
|
||||
<a href="index.php">Back</a>
|
||||
<br />
|
||||
|
||||
<?php
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if (isset($_POST['account_name']) && isset($_POST['account_description'])) {
|
||||
// WePay sanitizes its own data, but displaying raw POST data on your own site is a XSS security hole.
|
||||
$name = htmlentities($_POST['account_name']);
|
||||
$desc = htmlentities($_POST['account_description']);
|
||||
try {
|
||||
$wepay = new WePay($_SESSION['wepay_access_token']);
|
||||
$account = $wepay->request('account/create', array(
|
||||
'name' => $name,
|
||||
'description' => $desc,
|
||||
));
|
||||
echo "Created account $name for '$desc'! View on WePay at <a href=\"$account->account_uri\">$account->account_uri</a>. See all of your accounts <a href=\"accountlist.php\">here</a>.";
|
||||
}
|
||||
catch (WePayException $e) {
|
||||
// Something went wrong - normally you would log
|
||||
// this and give your user a more informative message
|
||||
echo $e->getMessage();
|
||||
}
|
||||
}
|
||||
else {
|
||||
echo 'Account name and description are both required.';
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<form method="post">
|
||||
<fieldset>
|
||||
<legend>Account Info</legend>
|
||||
|
||||
<label for="account_name">Account Name:</label><br />
|
||||
<input type="text" id="account_name" name="account_name" placeholder="Ski Trip Savings"/>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<label for="account_description">Account Description: </label><br />
|
||||
<textarea name="account_description" rows="10" cols="40" placeholder="Saving up some dough for our ski trip!"></textarea>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<input type="submit" value="Open account" />
|
||||
</fieldset>
|
||||
</form>
|
22
externals/wepay/demoapp/user.php
vendored
Normal file
22
externals/wepay/demoapp/user.php
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
require './_shared.php';
|
||||
?>
|
||||
<h1>WePay Demo App: User Info</h1>
|
||||
<a href="index.php">Back</a>
|
||||
<br />
|
||||
|
||||
<?php
|
||||
try {
|
||||
$wepay = new WePay($_SESSION['wepay_access_token']);
|
||||
$user = $wepay->request('user');
|
||||
echo '<dl>';
|
||||
foreach ($user as $key => $value) {
|
||||
echo "<dt>$key</dt><dd>$value</dd>";
|
||||
}
|
||||
echo '</dl>';
|
||||
}
|
||||
catch (WePayException $e) {
|
||||
// Something went wrong - normally you would log
|
||||
// this and give your user a more informative message
|
||||
echo $e->getMessage();
|
||||
}
|
69
externals/wepay/iframe_demoapp/checkout.php
vendored
Executable file
69
externals/wepay/iframe_demoapp/checkout.php
vendored
Executable file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* This PHP script helps you do the iframe checkout
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Put your API credentials here:
|
||||
* Get these from your API app details screen
|
||||
* https://stage.wepay.com/app
|
||||
*/
|
||||
$client_id = "PUT YOUR CLIENT_ID HERE";
|
||||
$client_secret = "PUT YOUR CLIENT_SECRET HERE";
|
||||
$access_token = "PUT YOUR ACCESS TOKEN HERE";
|
||||
$account_id = "PUT YOUR ACCOUNT_ID HERE"; // you can find your account ID via list_accounts.php which users the /account/find call
|
||||
|
||||
/**
|
||||
* Initialize the WePay SDK object
|
||||
*/
|
||||
require '../wepay.php';
|
||||
Wepay::useStaging($client_id, $client_secret);
|
||||
$wepay = new WePay($access_token);
|
||||
|
||||
/**
|
||||
* Make the API request to get the checkout_uri
|
||||
*
|
||||
*/
|
||||
try {
|
||||
$checkout = $wepay->request('/checkout/create', array(
|
||||
'account_id' => $account_id, // ID of the account that you want the money to go to
|
||||
'amount' => 100, // dollar amount you want to charge the user
|
||||
'short_description' => "this is a test payment", // a short description of what the payment is for
|
||||
'type' => "GOODS", // the type of the payment - choose from GOODS SERVICE DONATION or PERSONAL
|
||||
'mode' => "iframe", // put iframe here if you want the checkout to be in an iframe, regular if you want the user to be sent to WePay
|
||||
)
|
||||
);
|
||||
} catch (WePayException $e) { // if the API call returns an error, get the error message for display later
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>Checkout:</h1>
|
||||
|
||||
<p>The user will checkout here:</p>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<h2 style="color:red">ERROR: <?php echo $error ?></h2>
|
||||
<?php else: ?>
|
||||
<div id="checkout_div"></div>
|
||||
|
||||
<script type="text/javascript" src="https://stage.wepay.com/js/iframe.wepay.js">
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
WePay.iframe_checkout("checkout_div", "<?php echo $checkout->checkout_uri ?>");
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
74
externals/wepay/iframe_demoapp/list_accounts.php
vendored
Executable file
74
externals/wepay/iframe_demoapp/list_accounts.php
vendored
Executable file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* This PHP script helps you find your account_id
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Put your API credentials here:
|
||||
* Get these from your API app details screen
|
||||
* https://stage.wepay.com/app
|
||||
*/
|
||||
$client_id = "PUT YOUR CLIENT_ID HERE";
|
||||
$client_secret = "PUT YOUR CLIENT_SECRET HERE";
|
||||
$access_token = "PUT YOUR ACCESS TOKEN HERE";
|
||||
|
||||
/**
|
||||
* Initialize the WePay SDK object
|
||||
*/
|
||||
require '../wepay.php';
|
||||
Wepay::useStaging($client_id, $client_secret);
|
||||
$wepay = new WePay($access_token);
|
||||
|
||||
/**
|
||||
* Make the API request to get a list of all accounts this user owns
|
||||
*
|
||||
*/
|
||||
try {
|
||||
$accounts = $wepay->request('/account/find');
|
||||
} catch (WePayException $e) { // if the API call returns an error, get the error message for display later
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>List all accounts:</h1>
|
||||
|
||||
<p>The following is a list of all accounts that this user owns</p>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<h2 style="color:red">ERROR: <?php echo $error ?></h2>
|
||||
<?php elseif (empty($accounts)) : ?>
|
||||
<h2>You do not have any accounts. Go to <a href="https://stage.wepay.com.com">https://stage.wepay.com</a> to open an account.<h2>
|
||||
<?php else: ?>
|
||||
<table border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Account ID</td>
|
||||
<td>Account Name</td>
|
||||
<td>Account Description</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($accounts as $a): ?>
|
||||
<tr>
|
||||
<td><?php echo $a->account_id ?></td>
|
||||
<td><?php echo $a->name ?></td>
|
||||
<td><?php echo $a->description ?></td>
|
||||
</tr>
|
||||
<?php endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
294
externals/wepay/wepay.php
vendored
Executable file
294
externals/wepay/wepay.php
vendored
Executable file
|
@ -0,0 +1,294 @@
|
|||
<?php
|
||||
|
||||
class WePay {
|
||||
|
||||
/**
|
||||
* Version number - sent in user agent string
|
||||
*/
|
||||
const VERSION = '0.1.3';
|
||||
|
||||
/**
|
||||
* Scope fields
|
||||
* Passed into Wepay::getAuthorizationUri as array
|
||||
*/
|
||||
const SCOPE_MANAGE_ACCOUNTS = 'manage_accounts'; // Open and interact with accounts
|
||||
const SCOPE_VIEW_BALANCE = 'view_balance'; // View account balances
|
||||
const SCOPE_COLLECT_PAYMENTS = 'collect_payments'; // Create and interact with checkouts
|
||||
const SCOPE_VIEW_USER = 'view_user'; // Get details about authenticated user
|
||||
const SCOPE_PREAPPROVE_PAYMENTS = 'preapprove_payments'; // Create and interact with preapprovals
|
||||
const SCOPE_SEND_MONEY = 'send_money'; // For withdrawals
|
||||
|
||||
/**
|
||||
* Application's client ID
|
||||
*/
|
||||
private static $client_id;
|
||||
|
||||
/**
|
||||
* Application's client secret
|
||||
*/
|
||||
private static $client_secret;
|
||||
|
||||
/**
|
||||
* @deprecated Use WePay::getAllScopes() instead.
|
||||
*/
|
||||
public static $all_scopes = array(
|
||||
self::SCOPE_MANAGE_ACCOUNTS,
|
||||
self::SCOPE_VIEW_BALANCE,
|
||||
self::SCOPE_COLLECT_PAYMENTS,
|
||||
self::SCOPE_PREAPPROVE_PAYMENTS,
|
||||
self::SCOPE_VIEW_USER,
|
||||
self::SCOPE_SEND_MONEY,
|
||||
);
|
||||
|
||||
/**
|
||||
* Determines whether to use WePay's staging or production servers
|
||||
*/
|
||||
private static $production = null;
|
||||
|
||||
/**
|
||||
* cURL handle
|
||||
*/
|
||||
private static $ch = NULL;
|
||||
|
||||
/**
|
||||
* Authenticated user's access token
|
||||
*/
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* Pass WePay::getAllScopes() into getAuthorizationUri if your application desires full access
|
||||
*/
|
||||
public static function getAllScopes() {
|
||||
return array(
|
||||
self::SCOPE_MANAGE_ACCOUNTS,
|
||||
self::SCOPE_VIEW_BALANCE,
|
||||
self::SCOPE_COLLECT_PAYMENTS,
|
||||
self::SCOPE_PREAPPROVE_PAYMENTS,
|
||||
self::SCOPE_VIEW_USER,
|
||||
self::SCOPE_SEND_MONEY,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate URI used during oAuth authorization
|
||||
* Redirect your user to this URI where they can grant your application
|
||||
* permission to make API calls
|
||||
* @link https://www.wepay.com/developer/reference/oauth2
|
||||
* @param array $scope List of scope fields for which your application wants access
|
||||
* @param string $redirect_uri Where user goes after logging in at WePay (domain must match application settings)
|
||||
* @param array $options optional user_name,user_email which will be pre-filled on login form, state to be returned in querystring of redirect_uri
|
||||
* @return string URI to which you must redirect your user to grant access to your application
|
||||
*/
|
||||
public static function getAuthorizationUri(array $scope, $redirect_uri, array $options = array()) {
|
||||
// This does not use WePay::getDomain() because the user authentication
|
||||
// domain is different than the API call domain
|
||||
if (self::$production === null) {
|
||||
throw new RuntimeException('You must initialize the WePay SDK with WePay::useStaging() or WePay::useProduction()');
|
||||
}
|
||||
$domain = self::$production ? 'https://www.wepay.com' : 'https://stage.wepay.com';
|
||||
$uri = $domain . '/v2/oauth2/authorize?';
|
||||
$uri .= http_build_query(array(
|
||||
'client_id' => self::$client_id,
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'scope' => implode(',', $scope),
|
||||
'state' => empty($options['state']) ? '' : $options['state'],
|
||||
'user_name' => empty($options['user_name']) ? '' : $options['user_name'],
|
||||
'user_email' => empty($options['user_email']) ? '' : $options['user_email'],
|
||||
), '', '&');
|
||||
return $uri;
|
||||
}
|
||||
|
||||
private static function getDomain() {
|
||||
if (self::$production === true) {
|
||||
return 'https://wepayapi.com/v2/';
|
||||
}
|
||||
elseif (self::$production === false) {
|
||||
return 'https://stage.wepayapi.com/v2/';
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException('You must initialize the WePay SDK with WePay::useStaging() or WePay::useProduction()');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange a temporary access code for a (semi-)permanent access token
|
||||
* @param string $code 'code' field from query string passed to your redirect_uri page
|
||||
* @param string $redirect_uri Where user went after logging in at WePay (must match value from getAuthorizationUri)
|
||||
* @return StdClass|false
|
||||
* user_id
|
||||
* access_token
|
||||
* token_type
|
||||
*/
|
||||
public static function getToken($code, $redirect_uri) {
|
||||
$params = (array(
|
||||
'client_id' => self::$client_id,
|
||||
'client_secret' => self::$client_secret,
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'code' => $code,
|
||||
'state' => '', // do not hardcode
|
||||
));
|
||||
$result = self::make_request('oauth2/token', $params);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure SDK to run against WePay's production servers
|
||||
* @param string $client_id Your application's client id
|
||||
* @param string $client_secret Your application's client secret
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function useProduction($client_id, $client_secret) {
|
||||
if (self::$production !== null) {
|
||||
throw new RuntimeException('API mode has already been set.');
|
||||
}
|
||||
self::$production = true;
|
||||
self::$client_id = $client_id;
|
||||
self::$client_secret = $client_secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure SDK to run against WePay's staging servers
|
||||
* @param string $client_id Your application's client id
|
||||
* @param string $client_secret Your application's client secret
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function useStaging($client_id, $client_secret) {
|
||||
if (self::$production !== null) {
|
||||
throw new RuntimeException('API mode has already been set.');
|
||||
}
|
||||
self::$production = false;
|
||||
self::$client_id = $client_id;
|
||||
self::$client_secret = $client_secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new API session
|
||||
* @param string $token - access_token returned from WePay::getToken
|
||||
*/
|
||||
public function __construct($token) {
|
||||
if ($token && !is_string($token)) {
|
||||
throw new InvalidArgumentException('$token must be a string, ' . gettype($token) . ' provided');
|
||||
}
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up cURL handle
|
||||
*/
|
||||
public function __destruct() {
|
||||
if (self::$ch) {
|
||||
curl_close(self::$ch);
|
||||
self::$ch = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create the cURL request and execute it
|
||||
*/
|
||||
private static function make_request($endpoint, $values, $headers = array())
|
||||
{
|
||||
self::$ch = curl_init();
|
||||
$headers = array_merge(array("Content-Type: application/json"), $headers); // always pass the correct Content-Type header
|
||||
curl_setopt(self::$ch, CURLOPT_USERAGENT, 'WePay v2 PHP SDK v' . self::VERSION);
|
||||
curl_setopt(self::$ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt(self::$ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt(self::$ch, CURLOPT_TIMEOUT, 30); // 30-second timeout, adjust to taste
|
||||
curl_setopt(self::$ch, CURLOPT_POST, !empty($values)); // WePay's API is not strictly RESTful, so all requests are sent as POST unless there are no request values
|
||||
|
||||
$uri = self::getDomain() . $endpoint;
|
||||
curl_setopt(self::$ch, CURLOPT_URL, $uri);
|
||||
|
||||
if (!empty($values)) {
|
||||
curl_setopt(self::$ch, CURLOPT_POSTFIELDS, json_encode($values));
|
||||
}
|
||||
|
||||
$raw = curl_exec(self::$ch);
|
||||
if ($errno = curl_errno(self::$ch)) {
|
||||
// Set up special handling for request timeouts
|
||||
if ($errno == CURLE_OPERATION_TIMEOUTED) {
|
||||
throw new WePayServerException("Timeout occurred while trying to connect to WePay");
|
||||
}
|
||||
throw new Exception('cURL error while making API call to WePay: ' . curl_error(self::$ch), $errno);
|
||||
}
|
||||
$result = json_decode($raw);
|
||||
|
||||
$error_code = null;
|
||||
if (isset($result->error_code)) {
|
||||
$error_code = $result->error_code;
|
||||
}
|
||||
|
||||
$httpCode = curl_getinfo(self::$ch, CURLINFO_HTTP_CODE);
|
||||
if ($httpCode >= 400) {
|
||||
if ($httpCode >= 500) {
|
||||
throw new WePayServerException($result->error_description, $httpCode, $result, $error_code);
|
||||
}
|
||||
switch ($result->error) {
|
||||
case 'invalid_request':
|
||||
throw new WePayRequestException($result->error_description, $httpCode, $result, $error_code);
|
||||
case 'access_denied':
|
||||
default:
|
||||
throw new WePayPermissionException($result->error_description, $httpCode, $result, $error_code);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make API calls against authenticated user
|
||||
* @param string $endpoint - API call to make (ex. 'user', 'account/find')
|
||||
* @param array $values - Associative array of values to send in API call
|
||||
* @return StdClass
|
||||
* @throws WePayException on failure
|
||||
* @throws Exception on catastrophic failure (non-WePay-specific cURL errors)
|
||||
*/
|
||||
public function request($endpoint, array $values = array()) {
|
||||
$headers = array();
|
||||
|
||||
if ($this->token) { // if we have an access_token, add it to the Authorization header
|
||||
$headers[] = "Authorization: Bearer $this->token";
|
||||
}
|
||||
|
||||
$result = self::make_request($endpoint, $values, $headers);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Different problems will have different exception types so you can
|
||||
* catch and handle them differently.
|
||||
*
|
||||
* WePayServerException indicates some sort of 500-level error code and
|
||||
* was unavoidable from your perspective. You may need to re-run the
|
||||
* call, or check whether it was received (use a "find" call with your
|
||||
* reference_id and make a decision based on the response)
|
||||
*
|
||||
* WePayRequestException indicates a development error - invalid endpoint,
|
||||
* erroneous parameter, etc.
|
||||
*
|
||||
* WePayPermissionException indicates your authorization token has expired,
|
||||
* was revoked, or is lacking in scope for the call you made
|
||||
*/
|
||||
class WePayException extends Exception {
|
||||
public function __construct($description = '', $http_code = FALSE, $response = FALSE, $code = 0, $previous = NULL)
|
||||
{
|
||||
$this->response = $response;
|
||||
|
||||
if (!defined('PHP_VERSION_ID')) {
|
||||
$version = explode('.', PHP_VERSION);
|
||||
define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 50300) {
|
||||
parent::__construct($description, $code);
|
||||
} else {
|
||||
parent::__construct($description, $code, $previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
class WePayRequestException extends WePayException {}
|
||||
class WePayPermissionException extends WePayException {}
|
||||
class WePayServerException extends WePayException {}
|
|
@ -1649,6 +1649,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
|
||||
'PhortuneTestExtraPaymentProvider' => 'applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php',
|
||||
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
|
||||
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
|
||||
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
|
||||
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
|
||||
'PhrequentListController' => 'applications/phrequent/controller/PhrequentListController.php',
|
||||
|
@ -3426,6 +3427,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneTestExtraPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhrequentController' => 'PhabricatorController',
|
||||
'PhrequentDAO' => 'PhabricatorLiskDAO',
|
||||
'PhrequentListController' => 'PhrequentController',
|
||||
|
|
|
@ -51,6 +51,18 @@ final class PhabricatorPhortuneConfigOptions
|
|||
->setHidden(true)
|
||||
->setDescription(
|
||||
pht('PayPal API signature.')),
|
||||
$this->newOption('phortune.wepay.client-id', 'string', null)
|
||||
->setLocked(true)
|
||||
->setDescription(pht('WePay application ID.')),
|
||||
$this->newOption('phortune.wepay.client-secret', 'string', null)
|
||||
->setHidden(true)
|
||||
->setDescription(pht('WePay application secret.')),
|
||||
$this->newOption('phortune.wepay.access-token', 'string', null)
|
||||
->setHidden(true)
|
||||
->setDescription(pht('WePay access token.')),
|
||||
$this->newOption('phortune.wepay.account-id', 'string', null)
|
||||
->setHidden(true)
|
||||
->setDescription(pht('WePay account ID.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider {
|
||||
|
||||
public function isEnabled() {
|
||||
return $this->getWePayClientID() &&
|
||||
$this->getWePayClientSecret() &&
|
||||
$this->getWePayAccessToken() &&
|
||||
$this->getWePayAccountID();
|
||||
}
|
||||
|
||||
public function getProviderType() {
|
||||
return 'wepay';
|
||||
}
|
||||
|
||||
public function getProviderDomain() {
|
||||
return 'wepay.com';
|
||||
}
|
||||
|
||||
public function getPaymentMethodDescription() {
|
||||
return pht('Credit Card or Bank Account');
|
||||
}
|
||||
|
||||
public function getPaymentMethodIcon() {
|
||||
return 'rsrc/phortune/wepay.png';
|
||||
}
|
||||
|
||||
public function getPaymentMethodProviderDescription() {
|
||||
return "WePay";
|
||||
}
|
||||
|
||||
|
||||
public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
|
||||
$type = $method->getMetadataValue('type');
|
||||
return ($type == 'wepay');
|
||||
}
|
||||
|
||||
protected function executeCharge(
|
||||
PhortunePaymentMethod $payment_method,
|
||||
PhortuneCharge $charge) {
|
||||
throw new Exception("!");
|
||||
}
|
||||
|
||||
private function getWePayClientID() {
|
||||
return PhabricatorEnv::getEnvConfig('phortune.wepay.client-id');
|
||||
}
|
||||
|
||||
private function getWePayClientSecret() {
|
||||
return PhabricatorEnv::getEnvConfig('phortune.wepay.client-secret');
|
||||
}
|
||||
|
||||
private function getWePayAccessToken() {
|
||||
return PhabricatorEnv::getEnvConfig('phortune.wepay.access-token');
|
||||
}
|
||||
|
||||
private function getWePayAccountID() {
|
||||
return PhabricatorEnv::getEnvConfig('phortune.wepay.account-id');
|
||||
}
|
||||
|
||||
|
||||
/* -( One-Time Payments )-------------------------------------------------- */
|
||||
|
||||
public function canProcessOneTimePayments() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function renderOneTimePaymentButton(
|
||||
PhortuneAccount $account,
|
||||
PhortuneCart $cart,
|
||||
PhabricatorUser $user) {
|
||||
|
||||
$uri = $this->getControllerURI(
|
||||
'checkout',
|
||||
array(
|
||||
'cartID' => $cart->getID(),
|
||||
));
|
||||
|
||||
return phabricator_form(
|
||||
$user,
|
||||
array(
|
||||
'action' => $uri,
|
||||
'method' => 'POST',
|
||||
),
|
||||
phutil_tag(
|
||||
'button',
|
||||
array(
|
||||
'class' => 'green',
|
||||
'type' => 'submit',
|
||||
),
|
||||
pht('Pay with WePay')));
|
||||
}
|
||||
|
||||
|
||||
/* -( Controllers )-------------------------------------------------------- */
|
||||
|
||||
|
||||
public function canRespondToControllerAction($action) {
|
||||
switch ($action) {
|
||||
case 'checkout':
|
||||
case 'charge':
|
||||
case 'cancel':
|
||||
return true;
|
||||
}
|
||||
return parent::canRespondToControllerAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class WePay
|
||||
*/
|
||||
public function processControllerRequest(
|
||||
PhortuneProviderController $controller,
|
||||
AphrontRequest $request) {
|
||||
|
||||
$cart = $controller->loadCart($request->getInt('cartID'));
|
||||
if (!$cart) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
require_once $root.'/externals/wepay/wepay.php';
|
||||
|
||||
WePay::useStaging(
|
||||
$this->getWePayClientID(),
|
||||
$this->getWePayClientSecret());
|
||||
|
||||
$wepay = new WePay($this->getWePayAccessToken());
|
||||
|
||||
switch ($controller->getAction()) {
|
||||
case 'checkout':
|
||||
$return_uri = $this->getControllerURI(
|
||||
'charge',
|
||||
array(
|
||||
'cartID' => $cart->getID(),
|
||||
));
|
||||
|
||||
$cancel_uri = $this->getControllerURI(
|
||||
'cancel',
|
||||
array(
|
||||
'cartID' => $cart->getID(),
|
||||
));
|
||||
|
||||
$total_in_cents = $cart->getTotalInCents();
|
||||
$price = PhortuneCurrency::newFromUSDCents($total_in_cents);
|
||||
|
||||
$params = array(
|
||||
'account_id' => $this->getWePayAccountID(),
|
||||
'short_description' => 'Services', // TODO
|
||||
'type' => 'SERVICE',
|
||||
'amount' => $price->formatBareValue(),
|
||||
'long_description' => 'Services', // TODO
|
||||
'reference_id' => $cart->getPHID(),
|
||||
'app_fee' => 0,
|
||||
'fee_payer' => 'Payee',
|
||||
'redirect_uri' => $return_uri,
|
||||
'fallback_uri' => $cancel_uri,
|
||||
'auto_capture' => false,
|
||||
'require_shipping' => 0,
|
||||
'shipping_fee' => 0,
|
||||
'charge_tax' => 0,
|
||||
'mode' => 'regular',
|
||||
'funding_sources' => 'bank,cc'
|
||||
);
|
||||
|
||||
$result = $wepay->request('checkout/create', $params);
|
||||
|
||||
// NOTE: We might want to store "$result->checkout_id" on the Cart.
|
||||
|
||||
$uri = new PhutilURI($result->checkout_uri);
|
||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||
case 'charge':
|
||||
|
||||
// NOTE: We get $_REQUEST['checkout_id'] here, but our parameters are
|
||||
// dropped so we should stop depending on them or shove them into the
|
||||
// URI.
|
||||
|
||||
var_dump($_REQUEST);
|
||||
break;
|
||||
case 'cancel':
|
||||
var_dump($_REQUEST);
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Exception("The rest of this isn't implemented yet.");
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue