Skip to content

Commit

Permalink
Setup with Stripe payment service
Browse files Browse the repository at this point in the history
  • Loading branch information
marienfressinaud committed Nov 22, 2019
1 parent 486b89f commit ed12d56
Show file tree
Hide file tree
Showing 9 changed files with 337 additions and 18 deletions.
63 changes: 51 additions & 12 deletions Controllers/billingController.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

use \Flus\services\Payplug;
use \Flus\services\Stripe;
use \Flus\models\Invoice;

class FreshExtension_billing_Controller extends FreshRSS_index_Controller {
Expand Down Expand Up @@ -119,15 +119,32 @@ public function renewAction() {

$username = Minz_Session::param('currentUser', '_');

Payplug::init(
$system_conf->billing['payplug_secret_key'],
$system_conf->billing['payplug_api_version']
);
$payment_service = Payplug::create($username, $frequency, $amount);
Stripe::init($system_conf->billing['stripe_secret_key']);
$payment_service = Stripe::create($username, $frequency, $amount);
$payment_service->syncStatus();
$payment_service->save();
$payment_service->pay();

Minz_Request::forward(
['c' => 'billing', 'a' => 'pay'], true
);
}
}

public function payAction() {
$system_conf = FreshRSS_Context::$system_conf;
$waiting_payment_id = $this->view->waiting_payment_id;
if ($waiting_payment_id === null) {
Minz_Request::forward(array(
'c' => 'billing',
'a' => 'index',
), true);
}

Minz_View::appendScript('https://js.stripe.com/v3/', false, true, false);

$this->view->_layout('redirection');
$this->view->stripe_public_key = $system_conf->billing['stripe_public_key'];
$this->view->checkout_session_id = $waiting_payment_id;
}

public function returnAction() {
Expand Down Expand Up @@ -173,6 +190,31 @@ public function returnAction() {
$this->view->payment = $payment_service->payment();
}

public function cancelAction() {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
}

invalidateHttpCache();

$waiting_payment_id = $this->view->waiting_payment_id;
if ($waiting_payment_id === null) {
Minz_Request::forward(array(
'c' => 'billing',
'a' => 'index',
), true);
}

$system_conf = FreshRSS_Context::$system_conf;
Stripe::init($system_conf->billing['stripe_secret_key']);
$payment_service = Stripe::retrieve($waiting_payment_id);
$payment_service->cancel();
$payment_service->save();

Minz_View::prependTitle('Annulation du paiement · ');
$this->view->payment = $payment_service->payment();
}

public function addressAction() {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
Expand Down Expand Up @@ -257,11 +299,8 @@ public function addressAction() {

private function acknowledgeWaitingPayment($waiting_payment_id) {
$system_conf = FreshRSS_Context::$system_conf;
Payplug::init(
$system_conf->billing['payplug_secret_key'],
$system_conf->billing['payplug_api_version']
);
$payment_service = Payplug::retrieve($waiting_payment_id);
Stripe::init($system_conf->billing['stripe_secret_key']);
$payment_service = Stripe::retrieve($waiting_payment_id);
$payment_service->syncStatus();
$payment_service->save();
return $payment_service;
Expand Down
7 changes: 3 additions & 4 deletions extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public function loader($class_name) {
$class_name = substr($class_name, 5);
$base_path = $this->getPath() . '/';
include($base_path . str_replace('\\', '/', $class_name) . '.php');
} elseif (strpos($class_name, 'Stripe') === 0) {
include($this->getPath() . '/lib/stripe-php/init.php');
} elseif (strpos($class_name, 'Payplug') === 0) {
$base_path = $this->getPath() . '/lib/payplug-php/lib/';
include($base_path . str_replace('\\', '/', $class_name) . '.php');
Expand Down Expand Up @@ -126,10 +128,7 @@ public static function blockIfOverdue() {
Minz_Request::is('index', 'tos') ||
Minz_Request::is('index', 'cgv') ||
Minz_Request::is('auth', 'logout') ||
Minz_Request::is('billing', 'index') ||
Minz_Request::is('billing', 'address') ||
Minz_Request::is('billing', 'renew') ||
Minz_Request::is('billing', 'return')
Minz_Request::controllerName() === 'billing'
);
if ($subscription_is_overdue && !$action_is_allowed) {
Minz_Request::forward(array(
Expand Down
28 changes: 28 additions & 0 deletions layout/redirection.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php FreshRSS::preLayout(); ?>
<!DOCTYPE html>
<html lang="<?php echo FreshRSS_Context::$user_conf->language; ?>" xml:lang="<?php echo FreshRSS_Context::$user_conf->language; ?>">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="initial-scale=1.0" />
<?php echo self::headStyle(); ?>
<?php echo self::headScript(); ?>
<link rel="shortcut icon" id="favicon" type="image/x-icon" sizes="16x16 64x64" href="<?php echo Minz_Url::display('/favicon.ico'); ?>" />
<link rel="icon msapplication-TileImage apple-touch-icon" type="image/png" sizes="256x256" href="<?php echo Minz_Url::display('/themes/icons/favicon-256.png'); ?>" />
<link rel="apple-touch-icon" href="<?php echo Minz_Url::display('/themes/icons/apple-touch-icon.png'); ?>" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="<?php echo FreshRSS_Context::$system_conf->title; ?>">
<meta name="msapplication-TileColor" content="#FFF" />
<?php echo self::headTitle(); ?>
<meta name="description" content="<?php echo htmlspecialchars(FreshRSS_Context::$name . ' | ' . FreshRSS_Context::$description, ENT_COMPAT, 'UTF-8'); ?>" />
</head>

<body class="fl-layout-redirection">
<div class="fl-layout-container">
<?php
flush();
$this->render();
?>
</div>
</body>
</html>
191 changes: 191 additions & 0 deletions services/Stripe.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?php

namespace Flus\services;

class Stripe {
public static function init($key) {
\Stripe\Stripe::setApiKey($key);
}

public static function create($username, $frequency, $amount) {
$user_conf = get_user_configuration($username);
$email = $user_conf->mail_login;

$return_url = \Minz_Url::display(
['c' => 'billing', 'a' => 'return'], 'php', true
);
$cancel_url = \Minz_Url::display(
['c' => 'billing', 'a' => 'cancel'], 'php', true
);

$session = \Stripe\Checkout\Session::create([
'customer_email' => $email,
'payment_method_types' => ['card'],
'line_items' => [[
'name' => 'Abonnement Flus',
'amount' => $amount * 100,
'currency' => 'eur',
'quantity' => 1,
]],
'payment_intent_data' => [
'metadata' => [
'username' => $username,
'frequency' => $frequency,
],
],
'success_url' => $return_url,
'cancel_url' => $cancel_url,
'expand' => ['payment_intent'],
]);

return new Stripe($session);
}

public static function retrieve($session_id) {
$session = \Stripe\Checkout\Session::retrieve([
'id' => $session_id,
'expand' => ['payment_intent'],
]);
return new Stripe($session);
}

private $session = null;
private $status = 'unknown';
private $invoice_number = '';

private function __construct($session) {
$this->session = $session;
}

public function syncStatus() {
$payment_intent = $this->session->payment_intent;
if ($payment_intent->status === 'succeeded') {
$this->status = 'paid';
} elseif ($payment_intent->status === 'canceled') {
$this->status = 'canceled';
} else {
$this->status = 'waiting';
}
}

public function cancel() {
if ($this->status !== 'waiting') {
$this->status = 'canceled';
$payment_intent = $this->session->payment_intent;
$payment_intent->cancel();
}
}

public function save() {
$user_conf = get_user_configuration($this->username());

$billing = $user_conf->billing;
$billing['payments'][$this->id()] = $this->payment();
$user_conf->billing = $billing;

return $user_conf->save();
}

public function payment() {
return array(
'type' => 'stripe',
'status' => $this->status,
'date' => $this->date(),
'frequency' => $this->frequency(),
'amount' => $this->amount(),
'invoice_number' => $this->invoice_number,
);
}

public function id() {
return $this->session->id;
}

public function isPaid() {
return $this->status === 'paid';
}

public function isCanceled() {
return $this->status === 'canceled';
}

public function isWaiting() {
return $this->status === 'waiting';
}

public function date() {
$payment_intent = $this->session->payment_intent;
return $payment_intent->created;
}

public function amount() {
$payment_intent = $this->session->payment_intent;
return $payment_intent->amount / 100;
}

public function address() {
$user_conf = get_user_configuration($this->username());
$address = $user_conf->billing['address'];
return [
'first_name' => $address['first_name'],
'last_name' => $address['last_name'],
'address1' => $address['address'],
'postcode' => $address['postcode'],
'city' => $address['city'],
];
}

public function username() {
return $this->session->payment_intent->metadata['username'];
}

public function frequency() {
return $this->session->payment_intent->metadata['frequency'];
}

public function generateInvoiceNumber() {
$invoices_path = DATA_PATH . '/extensions-data/xExtension-Flus/invoices';
$lock_path = $invoices_path . '/.lock';

$lock_file = fopen($lock_path, 'r+');

if (flock($lock_file, LOCK_EX)) {
$last_invoice_number = @fread($lock_file, filesize($lock_path));
$this->invoice_number = $this->getNextInvoiceNumber($last_invoice_number);
$this->save();

rewind($lock_file);
fwrite($lock_file, $this->invoice_number);

flock($lock_file, LOCK_UN);
}

fclose($lock_file);

return $this->invoice_number;
}

private function getNextInvoiceNumber($last_invoice_number) {
$current_date = getdate();
$year = $current_date['year'];
$month = $current_date['mon'];

$invoice_sequence = 1;
if ($last_invoice_number) {
list(
$last_invoice_year,
$last_invoice_month,
$last_invoice_sequence
) = array_map('intval', explode('-', $last_invoice_number));

if ($last_invoice_year === $year) {
$invoice_sequence = $last_invoice_sequence + 1;
}
}

$invoice_format = '%04d-%02d-%04d';
return sprintf(
$invoice_format, $year, $month, $invoice_sequence
);
}
}
14 changes: 14 additions & 0 deletions static/style.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
/* Layouts */
.fl-layout-redirection {
display: flex;
flex-direction: column;
justify-content: center;

font-size: 1.5rem;
text-align: center;
}

.fl-layout-redirection .fl-layout-container {
margin-top: -5rem;
}

/* Forms */
.form-group.form-group-radios label {
display: inline-block;
Expand Down
17 changes: 17 additions & 0 deletions views/billing/cancel.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
if (!$this->subscription_is_overdue) {
$this->partial('aside_configure');
}
?>

<div class="post">
<h1>Annulation du paiement</h1>

<p>
Le paiement a été annulé, il ne vous sera rien débité.
</p>

<p>
<a href="<?= _url('billing', 'index') ?>">← Retour à la facturation</a>
</p>
</div>
2 changes: 1 addition & 1 deletion views/billing/index.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</p>

<?php if ($this->waiting_payment_id) { ?>
<a class="btn btn-important" href="<?= _url('billing', 'return') ?>">
<a class="btn btn-important" href="<?= _url('billing', 'pay') ?>">
Vérifier l’état de votre paiement en attente
</a>
<?php } elseif ($this->subscription_end_is_soon) { ?>
Expand Down

0 comments on commit ed12d56

Please sign in to comment.