This repository has been archived by the owner on Nov 30, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 377
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #54 from ohmybrew/issue-25-charge-tracking
Issue 25 charge tracking
- Loading branch information
Showing
27 changed files
with
1,077 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
<?php | ||
|
||
namespace OhMyBrew\ShopifyApp\Jobs; | ||
|
||
use Carbon\Carbon; | ||
use Illuminate\Bus\Queueable; | ||
use Illuminate\Contracts\Queue\ShouldQueue; | ||
use Illuminate\Foundation\Bus\Dispatchable; | ||
use Illuminate\Queue\InteractsWithQueue; | ||
use Illuminate\Queue\SerializesModels; | ||
use OhMyBrew\ShopifyApp\Models\Charge; | ||
use OhMyBrew\ShopifyApp\Models\Shop; | ||
|
||
class AppUninstalledJob implements ShouldQueue | ||
{ | ||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||
|
||
/** | ||
* Shop's instance. | ||
* | ||
* @var string | ||
*/ | ||
protected $shop; | ||
|
||
/** | ||
* Shop's myshopify domain. | ||
* | ||
* @var string | ||
*/ | ||
protected $shopDomain; | ||
|
||
/** | ||
* The webhook data. | ||
* | ||
* @var object | ||
*/ | ||
protected $data; | ||
|
||
/** | ||
* Create a new job instance. | ||
* | ||
* @param string $shopDomain The shop's myshopify domain | ||
* @param object $data The webhook data (JSON decoded) | ||
* | ||
* @return void | ||
*/ | ||
public function __construct($shopDomain, $data) | ||
{ | ||
$this->data = $data; | ||
$this->shopDomain = $shopDomain; | ||
$this->shop = $this->findShop(); | ||
} | ||
|
||
/** | ||
* Execute the job. | ||
* | ||
* @return bool | ||
*/ | ||
public function handle() | ||
{ | ||
if (!$this->shop) { | ||
return false; | ||
} | ||
|
||
$this->softDeleteShop(); | ||
$this->cancelCharge(); | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Soft deletes the shop in the database. | ||
* | ||
* @return void | ||
*/ | ||
protected function softDeleteShop() | ||
{ | ||
$this->shop->delete(); | ||
$this->shop->charges()->delete(); | ||
} | ||
|
||
/** | ||
* Cancels a recurring or one-time charge. | ||
* | ||
* @return void | ||
*/ | ||
protected function cancelCharge() | ||
{ | ||
$lastCharge = $this->shop->charges() | ||
->withTrashed() | ||
->whereIn('type', [Charge::CHARGE_RECURRING, Charge::CHARGE_ONETIME]) | ||
->orderBy('created_at', 'desc') | ||
->first(); | ||
|
||
if ($lastCharge && !$lastCharge->isDeclined() && !$lastCharge->isCancelled()) { | ||
$lastCharge->status = 'cancelled'; | ||
$lastCharge->cancelled_on = Carbon::today()->format('Y-m-d'); | ||
$lastCharge->save(); | ||
} | ||
} | ||
|
||
/** | ||
* Finds the shop based on domain from the webhook. | ||
* | ||
* @return Shop|null | ||
*/ | ||
protected function findShop() | ||
{ | ||
return Shop::where(['shopify_domain' => $this->shopDomain])->first(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
<?php | ||
|
||
namespace OhMyBrew\ShopifyApp\Models; | ||
|
||
use Carbon\Carbon; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\SoftDeletes; | ||
|
||
class Charge extends Model | ||
{ | ||
use SoftDeletes; | ||
|
||
// Types of charges | ||
const CHARGE_RECURRING = 1; | ||
const CHARGE_ONETIME = 2; | ||
const CHARGE_USAGE = 3; | ||
const CHARGE_CREDIT = 4; | ||
|
||
/** | ||
* The attributes that should be mutated to dates. | ||
* | ||
* @var array | ||
*/ | ||
protected $dates = ['deleted_at']; | ||
|
||
/** | ||
* Gets the shop for the charge. | ||
* | ||
* @return OhMyBrew\ShopifyApp\Models\Shop | ||
*/ | ||
public function shop() | ||
{ | ||
return $this->belongsTo('OhMyBrew\ShopifyApp\Models\Shop'); | ||
} | ||
|
||
/** | ||
* Checks if the charge is a test. | ||
* | ||
* @return bool | ||
*/ | ||
public function isTest() | ||
{ | ||
return (bool) $this->test; | ||
} | ||
|
||
/** | ||
* Checks if the charge is a type. | ||
* | ||
* @param int $type The charge type. | ||
* | ||
* @return bool | ||
*/ | ||
public function isType(int $type) | ||
{ | ||
return (int) $this->type === $type; | ||
} | ||
|
||
/** | ||
* Checks if the charge is a trial-type charge. | ||
* | ||
* @return bool | ||
*/ | ||
public function isTrial() | ||
{ | ||
return !is_null($this->trial_ends_on); | ||
} | ||
|
||
/** | ||
* Checks if the charge is currently in trial. | ||
* | ||
* @return bool | ||
*/ | ||
public function isActiveTrial() | ||
{ | ||
return $this->isTrial() && Carbon::today()->lte(Carbon::parse($this->trial_ends_on)); | ||
} | ||
|
||
/** | ||
* Returns the remaining trial days. | ||
* | ||
* @return int | ||
*/ | ||
public function remainingTrialDays() | ||
{ | ||
if (!$this->isTrial()) { | ||
return; | ||
} | ||
|
||
return $this->isActiveTrial() ? Carbon::today()->diffInDays($this->trial_ends_on) : 0; | ||
} | ||
|
||
/** | ||
* Returns the remaining trial days from cancellation date. | ||
* | ||
* @return int | ||
*/ | ||
public function remainingTrialDaysFromCancel() | ||
{ | ||
if (!$this->isTrial()) { | ||
return; | ||
} | ||
|
||
$cancelledDate = Carbon::parse($this->cancelled_on); | ||
$trialEndsDate = Carbon::parse($this->trial_ends_on); | ||
|
||
// Ensure cancelled date happened before the trial was supposed to end | ||
if ($this->isCancelled() && $cancelledDate->lte($trialEndsDate)) { | ||
// Diffeence the two dates and subtract from the total trial days to get whats remaining | ||
return $this->trial_days - ($this->trial_days - $cancelledDate->diffInDays($trialEndsDate)); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
/** | ||
* Returns the used trial days. | ||
* | ||
* @return int|null | ||
*/ | ||
public function usedTrialDays() | ||
{ | ||
if (!$this->isTrial()) { | ||
return; | ||
} | ||
|
||
return $this->trial_days - $this->remainingTrialDays(); | ||
} | ||
|
||
/** | ||
* Checks if the charge is active. | ||
* | ||
* @return bool | ||
*/ | ||
public function isActive() | ||
{ | ||
return $this->status === 'active'; | ||
} | ||
|
||
/** | ||
* Checks if the charge was accepted (for one-time and reccuring). | ||
* | ||
* @return bool | ||
*/ | ||
public function isAccepted() | ||
{ | ||
return $this->status === 'accepted'; | ||
} | ||
|
||
/** | ||
* Checks if the charge was declined (for one-time and reccuring). | ||
* | ||
* @return bool | ||
*/ | ||
public function isDeclined() | ||
{ | ||
return $this->status === 'declined'; | ||
} | ||
|
||
/** | ||
* Checks if the charge was cancelled. | ||
* | ||
* @return bool | ||
*/ | ||
public function isCancelled() | ||
{ | ||
return !is_null($this->cancelled_on) || $this->status === 'cancelled'; | ||
} | ||
|
||
/** | ||
* Checks if the charge is "active" (non-API check). | ||
* | ||
* @return bool | ||
*/ | ||
public function isOngoing() | ||
{ | ||
return $this->isActive() && !$this->isCancelled(); | ||
} | ||
} |
Oops, something went wrong.