mvp
This commit is contained in:
@ -19,6 +19,8 @@ use TheTempusProject\Classes\AdminController;
|
||||
use TheTempusProject\Plugins\Members as MemberModel;
|
||||
use TheTempusProject\Houdini\Classes\Issues;
|
||||
use TheTempusProject\Bedrock\Functions\Input;
|
||||
use TheTempusProject\Hermes\Functions\Route as Routes;
|
||||
use TheTempusProject\Hermes\Functions\Redirect;
|
||||
|
||||
class Members extends AdminController {
|
||||
public function __construct() {
|
||||
@ -28,13 +30,46 @@ class Members extends AdminController {
|
||||
}
|
||||
|
||||
public function index( $data = null ) {
|
||||
self::$title = 'Admin - Membership Scripts';
|
||||
Views::view( 'members.admin.scripts' );
|
||||
}
|
||||
|
||||
public function orphans( $data = null, $id = null ) {
|
||||
self::$title = 'Admin - Orphaned PRoducts';
|
||||
if ( $data = 'abandon' && ! empty( $id ) ) {
|
||||
MemberModel::orphanAbandon( $id );
|
||||
Redirect::to('admin/members/orphans');
|
||||
}
|
||||
$orphans = MemberModel::findOrphans();
|
||||
Views::view( 'members.admin.orphans', $orphans );
|
||||
}
|
||||
|
||||
public function webhooks( $data = null, $id = null ) {
|
||||
self::$title = 'Admin - Membership Webhooks';
|
||||
|
||||
if ( $data = 'delete' && ! empty( $id ) ) {
|
||||
MemberModel::webhookRemove( $id );
|
||||
Redirect::to('admin/members/webhooks');
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$webhooks = MemberModel::webhookList();
|
||||
foreach ($webhooks->data as $key => $webhook) {
|
||||
$hook = new \stdClass;
|
||||
$hook->id = $webhook->id;
|
||||
$hook->enabled_events = implode( ', <br>', $webhook->enabled_events );
|
||||
$hook->status = $webhook->status;
|
||||
$hook->url = $webhook->url;
|
||||
$data[] = $hook;
|
||||
}
|
||||
Components::set( 'urltouse', Routes::getAddress() );
|
||||
|
||||
if ( !Input::exists( 'submit' ) ) {
|
||||
return Views::view( 'members.admin.webhooks' );
|
||||
return Views::view( 'members.admin.webhooks', $data );
|
||||
}
|
||||
MemberModel::webhookSetup();
|
||||
Issues::add( 'success', 'Webhooks Generated' );
|
||||
Issues::add( 'error', 'Now, LEAVE!' );
|
||||
Redirect::to('admin/members/webhooks');
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class Member extends Controller {
|
||||
}
|
||||
try {
|
||||
$session = self::$stripe->billingPortal->sessions->create([
|
||||
'customer' => $customer,
|
||||
'customer' => $customer->stripe_customer,
|
||||
'return_url' => Routes::getAddress() . 'member/manage',
|
||||
]);
|
||||
} catch (\Stripe\Exception\InvalidRequestException $e) {
|
||||
@ -174,7 +174,7 @@ class Member extends Controller {
|
||||
Session::flash( 'success', 'We aren\'t currently accepting new members, please check back soon!' );
|
||||
return Redirect::home();
|
||||
}
|
||||
Views::view( 'members.landing1', $product );
|
||||
Views::view( 'members.landing', $product );
|
||||
}
|
||||
|
||||
public function checkout( $plan = 'monthly' ) {
|
||||
@ -187,6 +187,19 @@ class Member extends Controller {
|
||||
Issues::add( 'error', 'no customer' );
|
||||
return $this->index();
|
||||
}
|
||||
|
||||
$plan = strtolower( $plan );
|
||||
if ( ! in_array( $plan, ['monthly','yearly','upgrade'] ) ) {
|
||||
Session::flash( 'error', 'Unknown plan' );
|
||||
return Redirect::to( 'home/index' );
|
||||
}
|
||||
if ( $plan === 'upgrade' ) {
|
||||
$plan = 'yearly';
|
||||
$successUrl = Routes::getAddress() . 'member/payment/upgrade?session_id={CHECKOUT_SESSION_ID}';
|
||||
} else {
|
||||
$successUrl = Routes::getAddress() . 'member/payment/complete?session_id={CHECKOUT_SESSION_ID}';
|
||||
}
|
||||
|
||||
$stripePrice = $this->findPrice( $plan );
|
||||
|
||||
$session = self::$stripe->checkout->sessions->create([
|
||||
@ -197,7 +210,7 @@ class Member extends Controller {
|
||||
'quantity' => 1,
|
||||
]],
|
||||
'mode' => 'subscription',
|
||||
'success_url' => Routes::getAddress() . 'member/payment/complete?session_id={CHECKOUT_SESSION_ID}',
|
||||
'success_url' => $successUrl,
|
||||
'cancel_url' => Routes::getAddress() . 'member/payment/cancel',
|
||||
]);
|
||||
header('Location: ' . $session->url);
|
||||
@ -206,7 +219,7 @@ class Member extends Controller {
|
||||
|
||||
public function payment( $type = '' ) {
|
||||
$type = strtolower( $type );
|
||||
if ( ! in_array( $type, ['cancel','complete'] ) ) {
|
||||
if ( ! in_array( $type, ['cancel','complete','upgrade'] ) ) {
|
||||
Session::flash( 'error', 'Unknown Payment' );
|
||||
return Redirect::to( 'home/index' );
|
||||
}
|
||||
@ -216,6 +229,11 @@ class Member extends Controller {
|
||||
return Views::view( 'members.paymentcanceled' );
|
||||
}
|
||||
|
||||
if ( $type == 'upgrade' ) {
|
||||
self::$title = 'Better Members Area';
|
||||
return Views::view( 'members.upgradeCompleted' );
|
||||
}
|
||||
|
||||
self::$title = '(almost) Members Area';
|
||||
Views::view( 'members.paymentcomplete' );
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ class MembershipProducts extends DatabaseModel {
|
||||
public $databaseMatrix = [
|
||||
[ 'name', 'varchar', '128' ],
|
||||
[ 'description', 'text', '' ],
|
||||
[ 'monthly_price', 'int', '10' ], // must be int value greater than 99 IE $1.00 === 100, $4399.22 === 439922
|
||||
[ 'yearly_price', 'int', '10' ], // must be int value greater than 99 IE $1.00 === 100, $4399.22 === 439922
|
||||
[ 'monthly_price', 'int', '10' ], // must be int value greater than 99 IE $1.00 === 100, $4,399.22 === 439922
|
||||
[ 'yearly_price', 'int', '10' ], // must be int value greater than 99 IE $1.00 === 100, $4,399.22 === 439922
|
||||
[ 'stripe_product', 'varchar', '64' ], // not-required to create - generated by stripe after
|
||||
[ 'stripe_price_monthly', 'varchar', '64' ], // not-required to create - generated by stripe after
|
||||
[ 'stripe_price_yearly', 'varchar', '64' ], // not-required to create - generated by stripe after
|
||||
@ -166,7 +166,7 @@ class MembershipProducts extends DatabaseModel {
|
||||
|
||||
public function updateStripeYearlyPrice( $product, $yearly_price ) {
|
||||
$stripe->prices->update(
|
||||
$yearly->id,
|
||||
$yearly_price,
|
||||
['lookup_key' => ""]
|
||||
);
|
||||
return $this->createStripeYearlyPrice( $product, $yearly_price );
|
||||
@ -174,7 +174,7 @@ class MembershipProducts extends DatabaseModel {
|
||||
|
||||
public function updateStripeMonthlyPrice( $product, $monthly_price ) {
|
||||
$stripe->prices->update(
|
||||
$monthly->id,
|
||||
$monthly_price,
|
||||
['lookup_key' => ""]
|
||||
);
|
||||
return $this->createStripeMonthlyPrice( $product, $monthly_price );
|
||||
@ -208,7 +208,7 @@ class MembershipProducts extends DatabaseModel {
|
||||
if ( ! $data->count() ) {
|
||||
return false;
|
||||
}
|
||||
return $data->first();
|
||||
return $this->filter( $data->first() );
|
||||
}
|
||||
|
||||
public function mainProduct() {
|
||||
|
@ -19,6 +19,7 @@ use TheTempusProject\Bedrock\Classes\Config;
|
||||
use TheTempusProject\Hermes\Functions\Route as Routes;
|
||||
use TheTempusProject\Models\Memberships;
|
||||
use TheTempusProject\Models\MembershipProducts as Products;
|
||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||
|
||||
class Members extends Plugin {
|
||||
public static $stripe;
|
||||
@ -46,17 +47,17 @@ class Members extends Plugin {
|
||||
'text' => '<i class="fa fa-fw fa-arrow-down"></i> Memberships',
|
||||
'url' => [
|
||||
[
|
||||
'text' => '<i class="fa fa-fw fa-database"></i> Products',
|
||||
'text' => '<i class="fa fa-fw fa-barcode"></i> Products',
|
||||
'url' => '{ROOT_URL}admin/products',
|
||||
],
|
||||
[
|
||||
'text' => '<i class="fa fa-fw fa-database"></i> Subscriptions',
|
||||
'text' => '<i class="fa fa-fw fa-bag-shopping"></i> Subscriptions',
|
||||
'url' => '{ROOT_URL}admin/records',
|
||||
],
|
||||
// [
|
||||
// 'text' => '<i class="fa fa-fw fa-database"></i> Invoices',
|
||||
// 'url' => '{ROOT_URL}admin/invoices',
|
||||
// ],
|
||||
[
|
||||
'text' => '<i class="fa fa-fw fa-code"></i> Scripts',
|
||||
'url' => '{ROOT_URL}admin/members',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@ -116,41 +117,52 @@ class Members extends Plugin {
|
||||
];
|
||||
|
||||
public function __construct( $load = false ) {
|
||||
if ( ! self::$loaded ) {
|
||||
App::$userCPlinks[] = (object) self::$userLinks;
|
||||
self::$loaded = true;
|
||||
if ( ! self::$loaded && $load ) {
|
||||
if ( App::$isLoggedIn ) {
|
||||
App::$isMember = $this->groupHasMemberAccess( App::$activeGroup );
|
||||
if ( empty( App::$isMember ) ) {
|
||||
App::$isMember = $this->userHasActiveMembership( App::$activeUser->ID );
|
||||
}
|
||||
}
|
||||
$this->filters[] = [
|
||||
'name' => 'member',
|
||||
'find' => '#{MEMBER}(.*?){/MEMBER}#is',
|
||||
'replace' => ( App::$isMember ? '$1' : '' ),
|
||||
'enabled' => true,
|
||||
];
|
||||
$this->filters[] = [
|
||||
'name' => 'nonmember',
|
||||
'find' => '#{NONMEMBER}(.*?){/NONMEMBER}#is',
|
||||
'replace' => ( App::$isLoggedIn && App::$isMember == false ? '$1' : '' ),
|
||||
'enabled' => true,
|
||||
];
|
||||
$this->filters[] = [
|
||||
'name' => 'upgrade',
|
||||
'find' => '#{UPGRADE}(.*?){/UPGRADE}#is',
|
||||
'replace' => ( App::$isLoggedIn && ( App::$isMember === 'monthly' ) ? '$1' : '' ),
|
||||
'enabled' => true,
|
||||
];
|
||||
}
|
||||
if ( App::$isLoggedIn ) {
|
||||
App::$isMember = $this->groupHasMemberAccess( App::$activeGroup );
|
||||
if ( empty( App::$isMember ) ) {
|
||||
App::$isMember = $this->userHasActiveMembership( App::$activeUser->ID );
|
||||
|
||||
parent::__construct( $load );
|
||||
|
||||
if ( $this->checkEnabled() && App::$isLoggedIn ) {
|
||||
if ( ! self::$loaded && $load ) {
|
||||
App::$userCPlinks[] = (object) self::$userLinks;
|
||||
App::$topNavRightDropdown .= '<li><a href="{ROOT_URL}member/manage" class="dropdown-item"><i class="fa fa-fw fa-credit-card"></i> Subscriptions</a></li>';
|
||||
}
|
||||
}
|
||||
$this->filters[] = [
|
||||
'name' => 'member',
|
||||
'find' => '#{MEMBER}(.*?){/MEMBER}#is',
|
||||
'replace' => ( App::$isMember ? '$1' : '' ),
|
||||
'enabled' => true,
|
||||
];
|
||||
$this->filters[] = [
|
||||
'name' => 'nonmember',
|
||||
'find' => '#{NONMEMBER}(.*?){/NONMEMBER}#is',
|
||||
'replace' => ( App::$isLoggedIn && App::$isMember == false ? '$1' : '' ),
|
||||
'enabled' => true,
|
||||
];
|
||||
$this->filters[] = [
|
||||
'name' => 'upgrade',
|
||||
'find' => '#{UPGRADE}(.*?){/UPGRADE}#is',
|
||||
'replace' => ( App::$isLoggedIn && ( App::$isMember === 'monthly' ) ? '$1' : '' ),
|
||||
'enabled' => true,
|
||||
];
|
||||
|
||||
if ( ! self::$loaded && $load ) {
|
||||
self::$loaded = true;
|
||||
}
|
||||
|
||||
$api_key = Config::getValue( 'memberships/stripeSecret' );
|
||||
if ( $api_key == 'sk_xxxxxxxxxxxxxxx' || empty($api_key) ) {
|
||||
self::$stripe = false;
|
||||
} else {
|
||||
self::$stripe = new StripeClient( $api_key );
|
||||
}
|
||||
parent::__construct( $load );
|
||||
}
|
||||
|
||||
public function groupHasMemberAccess( $activeGroup ) {
|
||||
@ -169,6 +181,10 @@ class Members extends Plugin {
|
||||
|
||||
$products = new Products;
|
||||
$product = $products->findByPriceID( $membership->subscription_price_id );
|
||||
if ( empty( $product ) ) {
|
||||
Debug::error('Active membership on non-existent product');
|
||||
return false;
|
||||
}
|
||||
if ( $product->stripe_price_monthly == $membership->subscription_price_id ) {
|
||||
return 'monthly';
|
||||
}
|
||||
@ -177,7 +193,6 @@ class Members extends Plugin {
|
||||
|
||||
public static function webhookSetup() {
|
||||
$root = Routes::getAddress();
|
||||
// $root = "https://stripe.joeykimsey.com/";
|
||||
$response = self::$stripe->webhookEndpoints->create([
|
||||
'enabled_events' => self::$webhookEvents,
|
||||
'url' => $root . 'api/stripe/webhook',
|
||||
@ -185,13 +200,66 @@ class Members extends Plugin {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// public static function webhookSetup() {
|
||||
// $root = Routes::getAddress();
|
||||
// $root = "https://stripe.joeykimsey.com/";
|
||||
// $response = self::$stripe->webhookEndpoints->create([
|
||||
// 'enabled_events' => self::$webhookEvents,
|
||||
// 'url' => $root . 'api/stripe/webhook',
|
||||
// ]);
|
||||
// return $response;
|
||||
// }
|
||||
public static function webhookList() {
|
||||
$response = self::$stripe->webhookEndpoints->all(['limit' => 25]);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function webhookRemove( $id ) {
|
||||
$response = self::$stripe->webhookEndpoints->delete( $id, []);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function orphanAbandon( $id ) {
|
||||
$response = self::$stripe->prices->update(
|
||||
$id,
|
||||
['lookup_key' => ""]
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function findOrphans() {
|
||||
$orphans = [];
|
||||
// any prices with keys not represented in our local db will show as an orphan
|
||||
|
||||
$result = self::$stripe->prices->search([
|
||||
'query' => 'lookup_key:"membership-monthly"',
|
||||
]);
|
||||
|
||||
$products = new Products;
|
||||
if ( ! empty( $result->data ) ) {
|
||||
$product = $products->findByPriceID( $result->data[0]->id );
|
||||
|
||||
if ( empty( $product ) ) {
|
||||
$data = $result->data[0];
|
||||
|
||||
$child = new \stdClass;
|
||||
$child->price_id = $data->id;
|
||||
$child->product = $data->product;
|
||||
$child->lookup_key = $data->lookup_key;
|
||||
$child->amount = $data->unit_amount;
|
||||
$orphans[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
$result = self::$stripe->prices->search([
|
||||
'query' => 'lookup_key:"membership-yearly"',
|
||||
]);
|
||||
if ( ! empty( $result->data ) ) {
|
||||
$product = $products->findByPriceID( $result->data[0]->id );
|
||||
|
||||
if ( empty( $product ) ) {
|
||||
$data = $result->data[0];
|
||||
|
||||
$child = new \stdClass;
|
||||
$child->price_id = $data->id;
|
||||
$child->product = $data->product;
|
||||
$child->lookup_key = $data->lookup_key;
|
||||
$child->amount = $data->unit_amount;
|
||||
$orphans[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
return $orphans;
|
||||
}
|
||||
}
|
||||
|
35
app/plugins/members/views/admin/orphans.html
Normal file
35
app/plugins/members/views/admin/orphans.html
Normal file
@ -0,0 +1,35 @@
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Stripe Webhook Generation</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<p>Orphans are stripe prices that have unique lookup_keys used by the membership system, but have no products currently saved.</p>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%">price id</th>
|
||||
<th style="width: 10%">amount</th>
|
||||
<th style="width: 50%">lookup key</th>
|
||||
<th style="width: 10%">url</th>
|
||||
<th style="width: 10%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{LOOP}
|
||||
<tr>
|
||||
<td>{price_id}</td>
|
||||
<td>{amount}</td>
|
||||
<td>{lookup_key}</td>
|
||||
<td><a href="{ROOT_URL}admin/members/orphans/adopt/{price_id}" class="btn btn-sm btn-success"><i class="fa fa-fw fa-download"></i></a></td>
|
||||
<td><a href="{ROOT_URL}admin/members/orphans/abandon/{price_id}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</tr>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<tr>
|
||||
<td class="text-center" colspan="5">
|
||||
No results to show.
|
||||
</td>
|
||||
</tr>
|
||||
{/ALT}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -45,7 +45,7 @@
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg center-block">Send</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg center-block">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
13
app/plugins/members/views/admin/scripts.html
Normal file
13
app/plugins/members/views/admin/scripts.html
Normal file
@ -0,0 +1,13 @@
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Membership Scripts</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<a href="{ROOT_URL}admin/members/webhooks">Webhook Generation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{ROOT_URL}admin/members/orphans">Orphan Products</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
@ -1,10 +1,43 @@
|
||||
<legend>WARNING: Regenerating existing webhooks makes joey sad, don't do iit!</legend>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="submit" class="col-lg-3 control-label"></label>
|
||||
<div class="col-lg-3">
|
||||
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
|
||||
</div>
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Stripe Webhook Generation</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%">id</th>
|
||||
<th style="width: 10%">status</th>
|
||||
<th style="width: 50%">enabled_events</th>
|
||||
<th style="width: 10%">url</th>
|
||||
<th style="width: 10%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{LOOP}
|
||||
<tr>
|
||||
<td>{id}</td>
|
||||
<td>{status}</td>
|
||||
<td>{enabled_events}</td>
|
||||
<td>{url}</td>
|
||||
<td><a href="{ROOT_URL}admin/members/webhooks/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</tr>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<tr>
|
||||
<td class="text-center" colspan="5">
|
||||
No results to show.
|
||||
</td>
|
||||
</tr>
|
||||
{/ALT}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
<h5>WARNING: Regenerating existing webhooks makes joey sad, don't do iit!</h5>
|
||||
<p>The new webhooks will be generated using the base url: <strong>{urltouse}</strong></p>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-danger">re-generate</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -1,21 +1,20 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2 text-center">
|
||||
<h2>Are You Sure You Want to Cancel?</h2>
|
||||
<div class="container py-4 context-main-bg my-4">
|
||||
<h2 class="text-center">Are You Sure You Want to Cancel?</h2>
|
||||
<hr>
|
||||
<div class="text-center">
|
||||
<p class="lead">
|
||||
Cancelling your subscription means you'll miss out on exclusive features, updates, and benefits.
|
||||
Cancelling your subscription means you'll miss out on exclusive features, updates, and benefits.
|
||||
</p>
|
||||
<p>
|
||||
Consider staying to continue enjoying the full experience of our service.
|
||||
Consider staying to continue enjoying the full experience of our service.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="{ROOT_URL}member/cancelconfirm/{cancelid}" class="btn btn-lg btn-danger btn-block">Cancel Subscription</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="{ROOT_URL}member/manage" class="btn btn-lg btn-primary btn-block">Go Back</a>
|
||||
</div>
|
||||
<div class="col-md-3 offset-3">
|
||||
<a href="{ROOT_URL}member/cancelconfirm/{cancelid}" class="btn btn-lg btn-outline-danger btn-block">Cancel Subscription</a>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<a href="{ROOT_URL}member/manage" class="btn btn-lg btn-primary btn-block atb-green-bg">Go Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,4 +1,3 @@
|
||||
|
||||
<!-- Compare plans -->
|
||||
<div class="table-responsive pricing-container container pb-4" id="compare">
|
||||
<h1 class="display-6 text-center my-4">Compare plans</h1>
|
||||
@ -31,7 +30,7 @@
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-start">Share bookmarks abd folders</th>
|
||||
<th scope="row" class="text-start">Share bookmarks and folders</th>
|
||||
<td><i class="fa fa-fw fa-check"></i></td>
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
@ -88,9 +87,6 @@
|
||||
<li>Access from any device</li>
|
||||
<li>Share access with anyone</li>
|
||||
</ul>
|
||||
<a href="/register" class="mt-auto w-100 btn btn-lg atb-green-outline">
|
||||
Sign-Up for Free
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -100,7 +96,7 @@
|
||||
<h4 class="my-0 fw-normal">Monthly</h4>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h1 class="card-title pricing-card-title">$4.99<small class="text-muted fw-light">/month</small></h1>
|
||||
<h1 class="card-title pricing-card-title">{prettyPriceMonthly}<small class="text-muted fw-light">/month</small></h1>
|
||||
<ul class="list-unstyled mt-3 mb-4">
|
||||
<li>Import/Export Features</li>
|
||||
<li>Integration with TempusTools App (WIP)</li>
|
||||
@ -108,7 +104,7 @@
|
||||
<li>Direct control of Feature Development</li>
|
||||
<li>Early Access to new features</li>
|
||||
</ul>
|
||||
<a href="/member/signup/monthly" class="mt-auto w-100 btn btn-lg atb-green-bg">
|
||||
<a href="/member/checkout/monthly" class="mt-auto w-100 btn btn-lg atb-green-bg">
|
||||
Get started
|
||||
</a>
|
||||
</div>
|
||||
@ -120,11 +116,11 @@
|
||||
<h4 class="my-0 fw-normal">Yearly</h4>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h1 class="card-title pricing-card-title">$19.99<small class="text-muted fw-light">/year</small></h1>
|
||||
<h1 class="card-title pricing-card-title">{prettyPriceYearly}<small class="text-muted fw-light">/year</small></h1>
|
||||
<ul class="list-unstyled mt-3 mb-4">
|
||||
<li>Its cheaper if you like the product</li>
|
||||
</ul>
|
||||
<a href="/member/signup/yearly" class="mt-auto w-100 btn btn-lg atb-green-bg">
|
||||
<a href="/member/checkout/yearly" class="mt-auto w-100 btn btn-lg atb-green-bg">
|
||||
Get started
|
||||
</a>
|
||||
</div>
|
@ -1,7 +1,8 @@
|
||||
<div class="container py-4">
|
||||
<div class="container py-4 context-main-bg my-4">
|
||||
<h3 class="mb-4 text-center">Manage Memberships</h3>
|
||||
<hr>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<legend>Memberships</legend>
|
||||
<table class="table text-center context-main">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -23,13 +24,13 @@
|
||||
<td>{DTC=date}{current_period_start}{/DTC}</td>
|
||||
<td>{DTC=date}{current_period_end}{/DTC}</td>
|
||||
<td>
|
||||
<a href="{ROOT_URL}member/pause/{ID}" class="btn btn-sm btn-primary">
|
||||
<a href="{ROOT_URL}member/pause/{ID}" class="btn btn-sm btn-outline-warning">
|
||||
<i class="fa fa-fw fa-pause"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{ROOT_URL}member/cancel/{ID}" class="btn btn-sm btn-danger">
|
||||
<i class="fa fa-fw fa-trash"></i>
|
||||
<a href="{ROOT_URL}member/cancel/{ID}" class="btn btn-sm btn-outline-danger">
|
||||
<i class="fa fa-fw fa-ban"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@ -43,7 +44,7 @@
|
||||
{/ALT}
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="{ROOT_URL}member/managepayment" class="btn btn-sm btn-primary">
|
||||
<a href="{ROOT_URL}member/managepayment" class="btn btn-sm btn-primary atb-green-bg">
|
||||
Manage Payment Method
|
||||
</a>
|
||||
</div>
|
||||
|
@ -11,7 +11,8 @@
|
||||
Right now, this entire system was built and managed by myself. I have used my own version of this for years, but translating it to a publicly available product is not a 1-to-1 job. There may be bugs or issues encountered while you use the product. I can't guarantee a fix for every need in every case immediately, but I do actively keep track of bugs and work hard to ensure everyone has a great experience using the app.
|
||||
</p>
|
||||
<div class="text-center mt-4 pb-4">
|
||||
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5 atb-green-bg">Report a Bug</a>{/loggedin}
|
||||
{loggedin}<a href="/bugreport" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Report a Bug</a>{/loggedin}
|
||||
<a href="/member/manage" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-bg">Manage Membership</a>
|
||||
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
@ -1,21 +1,20 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2 text-center">
|
||||
<h2>Are You Sure You Want to Pause?</h2>
|
||||
<div class="container py-4 context-main-bg my-4">
|
||||
<h2 class="text-center">Are You Sure You Want to Pause?</h2>
|
||||
<hr>
|
||||
<div class="text-center">
|
||||
<p class="lead">
|
||||
Pausing your subscription means you'll miss out on exclusive features, updates, and benefits.
|
||||
Pausing your subscription means you'll miss out on exclusive features, updates, and benefits.
|
||||
</p>
|
||||
<p>
|
||||
Consider staying to continue enjoying the full experience of our service.
|
||||
Consider staying to continue enjoying the full experience of our service.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="{ROOT_URL}member/pauseconfirm/{pauseid}" class="btn btn-lg btn-danger btn-block">Pause Subscription</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="{ROOT_URL}member/manage" class="btn btn-lg btn-primary btn-block">Go Back</a>
|
||||
</div>
|
||||
<div class="col-md-3 offset-3">
|
||||
<a href="{ROOT_URL}member/pauseconfirm/{pauseid}" class="btn btn-lg btn-outline-danger btn-block">Pause Subscription</a>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<a href="{ROOT_URL}member/manage" class="btn btn-lg btn-primary btn-block atb-green-bg">Go Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,4 +1,14 @@
|
||||
<div class="jumbotron text-center">
|
||||
<h1>Take your time, its not a sprint, its a marathon.</h1>
|
||||
<p>Its ok, no-one wants to checkout too fast, its embarrassing.</p>
|
||||
<div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4">
|
||||
<h2 class="text-center atb-green mb-4">Almost there!</h2>
|
||||
<p class="lead">
|
||||
Take your time, its not a sprint, its a marathon.
|
||||
Nno-one wants to checkout too fast, its embarrassing.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
If you think this is a tool that you could really use and can't afford it, use the contact form below and tell me why you need it.
|
||||
</p>
|
||||
<div class="text-center mt-4 pb-4">
|
||||
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5 atb-green-bg">Report a Bug</a>{/loggedin}
|
||||
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,14 @@
|
||||
<div class="jumbotron text-center">
|
||||
<h1>Thanks for joining!</h1>
|
||||
<p>Its people like you who keep the pixels on around here.
|
||||
Its people like me who tell you life is unfair and it could take up to an hour for your membership to activate.</p>
|
||||
<div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4">
|
||||
<h2 class="text-center atb-green mb-4">Thanks for joining!</h2>
|
||||
<p class="lead">
|
||||
Its people like you who keep the pixels on around here.
|
||||
Its people like me who tell people like you that unfortunately, it could take up to an hour for your membership to activate.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
With that said, its usually instant, and if its taking too long, just reach out to us via the contact form.
|
||||
</p>
|
||||
<div class="text-center mt-4 pb-4">
|
||||
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5 atb-green-bg">Report a Bug</a>{/loggedin}
|
||||
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
@ -1,6 +1,6 @@
|
||||
<div class="col-8 mx-auto p-4 rounded shadow-sm mb-5 context-main-bg mt-4 text-center">
|
||||
<div class="row">
|
||||
<h2 class="text-primary mb-4">Upgrade to a Yearly Plan</h2>
|
||||
<h2 class="mb-4 atb-green">Upgrade to a Yearly Plan</h2>
|
||||
<p class="lead">
|
||||
Save more and enjoy uninterrupted access to all features with our yearly plan!
|
||||
</p>
|
||||
@ -10,12 +10,12 @@
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="/member/checkout/yearly" class="btn btn-lg btn-success btn-block">
|
||||
<a href="/member/checkout/upgrade" class="btn btn-lg btn-block atb-green-bg">
|
||||
Upgrade to Yearly
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="/member/upgrade" class="btn btn-lg btn-primary btn-block">
|
||||
<a href="/member" class="btn btn-lg btn-block atb-green-outline">
|
||||
Stay on Monthly
|
||||
</a>
|
||||
</div>
|
||||
|
13
app/plugins/members/views/upgradeCompleted.html
Normal file
13
app/plugins/members/views/upgradeCompleted.html
Normal file
@ -0,0 +1,13 @@
|
||||
<div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4">
|
||||
<h2 class="text-center atb-green mb-4">Thanks for the vote of confidence!</h2>
|
||||
<p class="lead">
|
||||
While you were more profitable as a monthly user, I do appreciate the vote of confidence it takes to commit to 4x the cost at once.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
There really is no more benefit than you saving money, so enjoy the features you have already come to know and enjoy.
|
||||
</p>
|
||||
<div class="text-center mt-4 pb-4">
|
||||
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5 atb-green-bg">Report a Bug</a>{/loggedin}
|
||||
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
Reference in New Issue
Block a user