This commit is contained in:
Atharva Deosthale
2025-11-12 19:09:55 +05:30
parent 350e651c53
commit fee6ad43d6
50 changed files with 291 additions and 296 deletions
-2
View File
@@ -130,5 +130,3 @@ return [
],
];
-1
View File
@@ -56,7 +56,6 @@ use Utopia\Storage\Storage;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
use Redis;
use Utopia\Validator\Hostname;
use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
@@ -40,5 +40,3 @@ interface Adapter
public function testConnection(array $config): ProviderTestResult;
}
@@ -10,5 +10,3 @@ class ProviderCheckoutSession
) {
}
}
@@ -10,5 +10,3 @@ class ProviderFeatureRef
) {
}
}
@@ -10,5 +10,3 @@ class ProviderPlanRef
) {
}
}
@@ -10,5 +10,3 @@ class ProviderPortalSession
) {
}
}
@@ -11,5 +11,3 @@ class ProviderState
) {
}
}
@@ -10,5 +10,3 @@ class ProviderSubscriptionRef
) {
}
}
@@ -11,5 +11,3 @@ class ProviderTestResult
) {
}
}
@@ -10,5 +10,3 @@ class ProviderUsageReport
) {
}
}
@@ -10,5 +10,3 @@ class ProviderWebhookResult
) {
}
}
@@ -43,5 +43,3 @@ class Registry
return $adapter;
}
}
@@ -2,10 +2,13 @@
namespace Appwrite\Payments\Provider;
use Swoole\Coroutine\Http\ClientProxy;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\System\System;
use Utopia\Database\Query;
use Utopia\System\System;
use function Swoole\Coroutine\Http\request;
class StripeAdapter implements Adapter
{
@@ -46,10 +49,12 @@ class StripeAdapter implements Adapter
'description' => 'Appwrite Payments Webhook for Project ' . $project->getId()
]);
$endpointData = json_decode($endpoint->getBody(), true);
$meta = [
'currency' => $account['default_currency'] ?? 'usd',
'webhookEndpointId' => $endpoint['id'] ?? null,
'webhookSecret' => $endpoint['secret'] ?? null,
'webhookEndpointId' => $endpointData['id'] ?? null,
'webhookSecret' => $endpointData['secret'] ?? null,
];
return new ProviderState($this->getIdentifier(), $config, $meta);
}
@@ -59,6 +64,17 @@ class StripeAdapter implements Adapter
$apiKey = (string) ($state->config['secretKey'] ?? '');
$name = (string) ($planData['name'] ?? '');
$description = (string) ($planData['description'] ?? '');
$providerPlanId = json_decode($planData['providers']['stripe']['planId'] ?? '', true);
// Check if product already exists
$existingProduct = $this->request($apiKey, 'GET', '/products/' . $providerPlanId);
if ($existingProduct->getStatusCode() === 200) {
$data = json_decode($existingProduct->getBody(), true);
return new ProviderPlanRef(externalPlanId: $data['id'], metadata: ['productId' => $data['id']]);
}
$product = $this->request($apiKey, 'POST', '/products', [
'name' => $name,
'description' => $description,
@@ -67,7 +83,8 @@ class StripeAdapter implements Adapter
'plan_id' => (string) ($planData['planId'] ?? '')
]
]);
$productId = (string) ($product['id'] ?? '');
$productData = json_decode($product->getBody(), true);
$productId = (string) ($productData['id'] ?? '');
$refs = ['productId' => $productId, 'prices' => []];
$pricing = (array) ($planData['pricing'] ?? []);
foreach ($pricing as $price) {
@@ -93,7 +110,7 @@ class StripeAdapter implements Adapter
{
$apiKey = (string) ($state->config['secretKey'] ?? '');
// 1) Update product name/description if provided
// Update product name/description if provided
$updates = [];
if (isset($planData['name']) && $planData['name'] !== '') {
$updates['name'] = (string) $planData['name'];
@@ -105,18 +122,21 @@ class StripeAdapter implements Adapter
$this->request($apiKey, 'POST', '/products/' . $reference->externalPlanId, $updates);
}
// 2) Reconcile prices: create new prices for provided pricing entries; deactivate orphaned
// Reconcile prices: create new prices for provided pricing entries; deactivate orphaned
$newPricing = (array) ($planData['pricing'] ?? []);
$existingPrices = (array) ($reference->metadata['prices'] ?? []);
// Fetch details for existing price ids
$existingMap = []; // key => priceId
foreach ($existingPrices as $pid) {
if (!$pid) { continue; }
if (!$pid) {
continue;
}
$price = $this->request($apiKey, 'GET', '/prices/' . $pid);
$currency = (string) ($price['currency'] ?? '');
$interval = (string) ($price['recurring']['interval'] ?? '');
$amount = (int) ($price['unit_amount'] ?? 0);
$priceData = json_decode($price->getBody(), true);
$currency = (string) ($priceData['currency'] ?? '');
$interval = (string) ($priceData['recurring']['interval'] ?? '');
$amount = (int) ($priceData['unit_amount'] ?? 0);
$key = $currency . ':' . $interval . ':' . $amount;
$existingMap[$key] = (string) $pid;
}
@@ -147,7 +167,8 @@ class StripeAdapter implements Adapter
'type' => 'payments_plan_price'
]
]);
$keptPriceIds[] = (string) ($res['id'] ?? '');
$resData = json_decode($res->getBody(), true);
$keptPriceIds[] = (string) ($resData['id'] ?? '');
}
// Deactivate any existing price not in desired set
@@ -250,8 +271,10 @@ class StripeAdapter implements Adapter
'metadata' => [ 'project_id' => $this->project->getId(), 'actor_id' => $actor->getId() ]
]);
$respData = json_decode($resp->getBody(), true);
// Map Stripe status to internal status
$stripeStatus = (string) ($resp['status'] ?? 'incomplete');
$stripeStatus = (string) ($respData['status'] ?? 'incomplete');
$statusMap = [
'active' => 'active',
'trialing' => 'trialing',
@@ -265,7 +288,7 @@ class StripeAdapter implements Adapter
$internalStatus = $statusMap[$stripeStatus] ?? 'pending';
return new ProviderSubscriptionRef(
externalSubscriptionId: (string) ($resp['id'] ?? ''),
externalSubscriptionId: (string) ($respData['id'] ?? ''),
metadata: ['status' => $internalStatus]
);
}
@@ -276,7 +299,8 @@ class StripeAdapter implements Adapter
$newPriceId = (string) ($changes['priceId'] ?? '');
if ($newPriceId !== '') {
$sub = $this->request($apiKey, 'GET', '/subscriptions/' . $subscription->externalSubscriptionId);
$itemId = $sub['items']['data'][0]['id'] ?? '';
$subData = json_decode($sub->getBody(), true);
$itemId = $subData['items']['data'][0]['id'] ?? '';
if ($itemId !== '') {
$this->request($apiKey, 'POST', '/subscriptions/' . $subscription->externalSubscriptionId, [
'items' => [ [ 'id' => $itemId, 'price' => $newPriceId ] ],
@@ -315,8 +339,9 @@ class StripeAdapter implements Adapter
'client_reference_id' => $actor->getId(),
'metadata' => [ 'project_id' => $this->project->getId(), 'actor_id' => $actor->getId() ]
];
$session = $this->request($apiKey, 'POST', '/checkout/sessions', $params);
return new ProviderCheckoutSession(url: (string) ($session['url'] ?? ''));
$sessionResponse = $this->request($apiKey, 'POST', '/checkout/sessions', $params);
$sessionData = json_decode($sessionResponse->getBody(), true);
return new ProviderCheckoutSession(url: (string) ($sessionData['url'] ?? ''));
}
public function createPortalSession(Document $actor, ProviderState $state, array $options = []): ProviderPortalSession
@@ -324,8 +349,9 @@ class StripeAdapter implements Adapter
$apiKey = (string) ($state->config['secretKey'] ?? '');
$returnUrl = (string) ($options['returnUrl'] ?? '');
$customerId = $this->ensureCustomer($apiKey, $actor);
$session = $this->request($apiKey, 'POST', '/billing_portal/sessions', [ 'customer' => $customerId, 'return_url' => $returnUrl ]);
return new ProviderPortalSession(url: (string) ($session['url'] ?? ''));
$sessionResponse = $this->request($apiKey, 'POST', '/billing_portal/sessions', [ 'customer' => $customerId, 'return_url' => $returnUrl ]);
$sessionData = json_decode($sessionResponse->getBody(), true);
return new ProviderPortalSession(url: (string) ($sessionData['url'] ?? ''));
}
public function reportUsage(ProviderSubscriptionRef $subscription, string $featureId, int $quantity, \DateTimeInterface $timestamp, ProviderState $state): void
@@ -337,7 +363,8 @@ class StripeAdapter implements Adapter
if ($stripeSubId !== '') {
try {
$sub = $this->request($apiKey, 'GET', '/subscriptions/' . $stripeSubId);
$customerId = (string) ($sub['customer'] ?? '');
$subData = json_decode($sub->getBody(), true);
$customerId = (string) ($subData['customer'] ?? '');
} catch (\Throwable $_) {
$customerId = '';
}
@@ -368,7 +395,9 @@ class StripeAdapter implements Adapter
$parts = [];
foreach (explode(',', $signature) as $part) {
[$k, $v] = array_pad(explode('=', trim($part), 2), 2, '');
if ($k !== '') { $parts[$k] = $v; }
if ($k !== '') {
$parts[$k] = $v;
}
}
$ts = (string) ($parts['t'] ?? '');
$v1 = (string) ($parts['v1'] ?? '');
@@ -413,8 +442,12 @@ class StripeAdapter implements Adapter
$prov = (array) ($providerMap['stripe'] ?? []);
if ((string) ($prov['subscriptionId'] ?? '') === $stripeSubId) {
$sub->setAttribute('status', $internalStatus);
if ($periodStart) $sub->setAttribute('currentPeriodStart', $periodStart);
if ($periodEnd) $sub->setAttribute('currentPeriodEnd', $periodEnd);
if ($periodStart) {
$sub->setAttribute('currentPeriodStart', $periodStart);
}
if ($periodEnd) {
$sub->setAttribute('currentPeriodEnd', $periodEnd);
}
$this->dbForPlatform->updateDocument('payments_subscriptions', $sub->getId(), $sub);
$changes['subscription'] = $sub->getId();
$changes['status'] = $internalStatus;
@@ -461,8 +494,9 @@ class StripeAdapter implements Adapter
{
$eventName = 'appwrite.payments.feature.usage.' . $projectId . '.' . $planId . '.' . $featureId;
$list = $this->request($apiKey, 'GET', '/billing/meters', ['limit' => 100]);
if (isset($list['data']) && is_array($list['data'])) {
foreach ($list['data'] as $m) {
$listData = json_decode($list->getBody(), true);
if (isset($listData['data']) && is_array($listData['data'])) {
foreach ($listData['data'] as $m) {
if ((string) ($m['event_name'] ?? '') === $eventName) {
$id = (string) ($m['id'] ?? '');
if ($id !== '' && (($m['active'] ?? true) === false)) {
@@ -479,19 +513,23 @@ class StripeAdapter implements Adapter
'value_settings' => [ 'event_payload_key' => 'value' ],
'customer_mapping' => [ 'type' => 'by_id', 'event_payload_key' => 'stripe_customer_id' ],
]);
return (string) ($meter['id'] ?? '');
$meterData = json_decode($meter->getBody(), true);
return (string) ($meterData['id'] ?? '');
}
private function ensureCustomer(string $apiKey, Document $actor): string
{
// Persist per-actor customer id in platform DB cache (users/teams) with dedicated attribute
$existing = (string) $actor->getAttribute('stripeCustomerId', '');
if ($existing !== '') return $existing;
if ($existing !== '') {
return $existing;
}
$customer = $this->request($apiKey, 'POST', '/customers', [
'email' => $actor->getAttribute('email', ''),
'metadata' => [ 'project_id' => $this->project->getId(), 'actor_id' => $actor->getId() ]
]);
$customerId = (string) ($customer['id'] ?? '');
$customerData = json_decode($customer->getBody(), true);
$customerId = (string) ($customerData['id'] ?? '');
try {
$actor->setAttribute('stripeCustomerId', $customerId);
$collection = $actor->getAttribute('kind', '') === 'team' ? 'teams' : 'users';
@@ -502,37 +540,39 @@ class StripeAdapter implements Adapter
return $customerId;
}
public function request(string $apiKey, string $method, string $path, array $params = []): array
public function request(string $apiKey, string $method, string $path, array $params = []): ClientProxy
{
$ch = \curl_init();
$url = 'https://api.stripe.com/v1' . $path;
$headers = [ 'Authorization: Bearer ' . $apiKey, 'Content-Type: application/x-www-form-urlencoded' ];
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
\curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
switch ($method) {
case 'GET':
if (!empty($params)) { $url .= '?' . http_build_query($params); }
break;
case 'POST':
\curl_setopt($ch, CURLOPT_POST, true);
if (!empty($params)) { \curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildFormData($params)); }
break;
case 'DELETE':
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
if (!empty($params)) { \curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildFormData($params)); }
break;
}
\curl_setopt($ch, CURLOPT_URL, $url);
$response = \curl_exec($ch);
$httpCode = (int) \curl_getinfo($ch, CURLINFO_HTTP_CODE);
\curl_close($ch);
if ($response === false) { throw new \RuntimeException('Stripe API request failed'); }
$data = \json_decode($response, true);
if ($httpCode >= 400) {
$message = is_array($data) && isset($data['error']['message']) ? (string) $data['error']['message'] : 'Stripe API error';
throw new \RuntimeException($message, $httpCode);
}
return is_array($data) ? $data : [];
// $ch = \curl_init();
// $url = 'https://api.stripe.com/v1' . $path;
// $headers = [ 'Authorization: Bearer ' . $apiKey, 'Content-Type: application/x-www-form-urlencoded' ];
// \curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// \curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// switch ($method) {
// case 'GET':
// if (!empty($params)) { $url .= '?' . http_build_query($params); }
// break;
// case 'POST':
// \curl_setopt($ch, CURLOPT_POST, true);
// if (!empty($params)) { \curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildFormData($params)); }
// break;
// case 'DELETE':
// \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
// if (!empty($params)) { \curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildFormData($params)); }
// break;
// }
// \curl_setopt($ch, CURLOPT_URL, $url);
// $response = \curl_exec($ch);
// $httpCode = (int) \curl_getinfo($ch, CURLINFO_HTTP_CODE);
// \curl_close($ch);
// if ($response === false) { throw new \RuntimeException('Stripe API request failed'); }
// $data = \json_decode($response, true);
// if ($httpCode >= 400) {
// $message = is_array($data) && isset($data['error']['message']) ? (string) $data['error']['message'] : 'Stripe API error';
// throw new \RuntimeException($message, $httpCode);
// }
// return is_array($data) ? $data : [];
$response = request($method, $path, $params, ['headers' => [ 'Authorization: Bearer ' . $apiKey, 'Content-Type: application/x-www-form-urlencoded' ]]);
return $response;
}
private function buildFormData(array $params, string $prefix = ''): string
@@ -549,5 +589,3 @@ class StripeAdapter implements Adapter
return implode('&', $data);
}
}
+1 -1
View File
@@ -6,11 +6,11 @@ use Appwrite\Platform\Modules\Console;
use Appwrite\Platform\Modules\Core;
use Appwrite\Platform\Modules\Databases;
use Appwrite\Platform\Modules\Functions;
use Appwrite\Platform\Modules\Payments;
use Appwrite\Platform\Modules\Projects;
use Appwrite\Platform\Modules\Proxy;
use Appwrite\Platform\Modules\Sites;
use Appwrite\Platform\Modules\Tokens;
use Appwrite\Platform\Modules\Payments;
use Utopia\Platform\Platform;
class Appwrite extends Platform
@@ -65,8 +65,7 @@ class Create extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -90,5 +89,3 @@ class Create extends Base
$response->json($created->getArrayCopy());
}
}
@@ -54,8 +54,7 @@ class Delete extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -78,5 +77,3 @@ class Delete extends Base
$response->noContent();
}
}
@@ -66,8 +66,7 @@ class Update extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -86,12 +85,16 @@ class Update extends Base
$response->json(['message' => 'Feature not found']);
return;
}
if ($name !== '') $feature->setAttribute('name', $name);
if ($type !== '') $feature->setAttribute('type', $type);
if ($description !== '') $feature->setAttribute('description', $description);
if ($name !== '') {
$feature->setAttribute('name', $name);
}
if ($type !== '') {
$feature->setAttribute('type', $type);
}
if ($description !== '') {
$feature->setAttribute('description', $description);
}
$feature = $dbForPlatform->updateDocument('payments_features', $feature->getId(), $feature);
$response->json($feature->getArrayCopy());
}
}
@@ -57,8 +57,7 @@ class XList extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
$filters = [ Query::equal('projectId', [$project->getId()]) ];
if ($search !== '') {
$filters[] = Query::search('name', $search);
@@ -70,5 +69,3 @@ class XList extends Base
]);
}
}
@@ -2,24 +2,24 @@
namespace Appwrite\Platform\Modules\Payments\Http\PlanFeatures;
use Appwrite\Event\Audit;
use Appwrite\Event\Event;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Appwrite\Event\Event;
use Appwrite\Event\Audit;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator\Text;
use Utopia\Validator\Integer;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Integer;
use Utopia\Validator\Text;
class Assign extends Base
{
@@ -89,8 +89,7 @@ class Assign extends Base
Document $project,
Event $queueForEvents,
Audit $queueForAudits
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -186,5 +185,3 @@ class Assign extends Base
$response->json($created->getArrayCopy());
}
}
@@ -2,14 +2,14 @@
namespace Appwrite\Platform\Modules\Payments\Http\PlanFeatures;
use Appwrite\Event\Audit;
use Appwrite\Event\Event;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Appwrite\Event\Event;
use Appwrite\Event\Audit;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
@@ -68,8 +68,7 @@ class Remove extends Base
Document $project,
Event $queueForEvents,
Audit $queueForAudits
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -123,5 +122,3 @@ class Remove extends Base
$response->noContent();
}
}
@@ -57,8 +57,7 @@ class XList extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
$items = $dbForPlatform->find('payments_plan_features', [
Query::equal('projectId', [$project->getId()]),
Query::equal('planId', [$planId])
@@ -69,5 +68,3 @@ class XList extends Base
]);
}
}
@@ -2,14 +2,18 @@
namespace Appwrite\Platform\Modules\Payments\Http\Plans;
use Appwrite\AppwriteException;
use Appwrite\Extend\Exception as ExtendException;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\Query;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Response;
use Exception;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Platform\Action;
@@ -78,8 +82,7 @@ class Create extends Base
Database $dbForProject,
Registry $registryPayments,
Document $project
)
{
) {
$document = new Document([
'projectId' => $project->getId(),
'projectInternalId' => $project->getSequence(),
@@ -104,6 +107,16 @@ class Create extends Base
return;
}
// Check if plan already exists
$existingPlan = $dbForPlatform->findOne('payments_plans', [
Query::equal('projectId', [$project->getId()]),
Query::equal('planId', [$planId])
]);
if ($existingPlan !== null && !$existingPlan->isEmpty()) {
// TODO: create a custom exception for this
return new AppwriteException(ExtendException::RESOURCE_ALREADY_EXISTS);
}
$created = $dbForPlatform->createDocument('payments_plans', $document);
// Provision on configured providers
@@ -134,5 +147,3 @@ class Create extends Base
$response->json($created->getArrayCopy());
}
}
@@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Payments\Http\Plans;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -55,8 +54,7 @@ class Delete extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -79,5 +77,3 @@ class Delete extends Base
$response->noContent();
}
}
@@ -57,8 +57,7 @@ class Get extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
$plan = $dbForPlatform->findOne('payments_plans', [
Query::equal('projectId', [$project->getId()]),
Query::equal('planId', [$planId])
@@ -73,5 +72,3 @@ class Get extends Base
$response->dynamic($plan, Response::MODEL_PAYMENT_PLAN);
}
}
@@ -2,12 +2,12 @@
namespace Appwrite\Platform\Modules\Payments\Http\Plans;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -78,8 +78,7 @@ class Update extends Base
Database $dbForProject,
Registry $registryPayments,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -98,11 +97,17 @@ class Update extends Base
$response->json(['message' => 'Plan not found']);
return;
}
if ($name !== '') $plan->setAttribute('name', $name);
if ($description !== '') $plan->setAttribute('description', $description);
if ($name !== '') {
$plan->setAttribute('name', $name);
}
if ($description !== '') {
$plan->setAttribute('description', $description);
}
$plan->setAttribute('isDefault', $isDefault);
$plan->setAttribute('isFree', $isFree);
if (!empty($pricing)) $plan->setAttribute('pricing', $pricing);
if (!empty($pricing)) {
$plan->setAttribute('pricing', $pricing);
}
$plan = $dbForPlatform->updateDocument('payments_plans', $plan->getId(), $plan);
// Update on providers if pricing changed or name/desc changed
@@ -129,5 +134,3 @@ class Update extends Base
$response->json($plan->getArrayCopy());
}
}
@@ -57,8 +57,7 @@ class XList extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
$filters = [ Query::equal('projectId', [$project->getId()]) ];
if ($search !== '') {
$filters[] = Query::search('search', $search);
@@ -71,5 +70,3 @@ class XList extends Base
$response->dynamic(new Document($payload), Response::MODEL_PAYMENT_PLAN_LIST);
}
}
@@ -2,12 +2,12 @@
namespace Appwrite\Platform\Modules\Payments\Http\Providers\Actions\Test;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Appwrite\Payments\Provider\Registry;
use Utopia\Database\Document;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
@@ -63,5 +63,3 @@ class Create extends Base
$response->json(['success' => $result->success, 'message' => $result->message]);
}
}
@@ -55,12 +55,14 @@ class Get extends Base
$payments = (array) $projectDoc->getAttribute('payments', []);
$providers = (array) ($payments['providers'] ?? []);
foreach ($providers as $pid => &$cfg) {
if (isset($cfg['secretKey'])) $cfg['secretKey'] = '***';
if (isset($cfg['webhookSecret'])) $cfg['webhookSecret'] = '***';
if (isset($cfg['secretKey'])) {
$cfg['secretKey'] = '***';
}
if (isset($cfg['webhookSecret'])) {
$cfg['webhookSecret'] = '***';
}
}
$payments['providers'] = $providers;
$response->json(['payments' => $payments]);
}
}
@@ -2,17 +2,16 @@
namespace Appwrite\Platform\Modules\Payments\Http\Providers;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator\ArrayList;
use Utopia\Validator\JSON as JSONValidator;
class Update extends Base
@@ -103,12 +102,14 @@ class Update extends Base
$out = (array) $updated->getAttribute('payments', []);
$prov = (array) ($out['providers'] ?? []);
foreach ($prov as $pid => &$cfg) {
if (isset($cfg['secretKey'])) $cfg['secretKey'] = '***';
if (isset($cfg['webhookSecret'])) $cfg['webhookSecret'] = '***';
if (isset($cfg['secretKey'])) {
$cfg['secretKey'] = '***';
}
if (isset($cfg['webhookSecret'])) {
$cfg['webhookSecret'] = '***';
}
}
$out['providers'] = $prov;
$response->json(['payments' => $out]);
}
}
@@ -2,11 +2,11 @@
namespace Appwrite\Platform\Modules\Payments\Http\Subscriptions;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -62,8 +62,7 @@ class Cancel extends Base
Document $user,
Registry $registryPayments,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -131,5 +130,3 @@ class Cancel extends Base
$response->noContent();
}
}
@@ -2,13 +2,13 @@
namespace Appwrite\Platform\Modules\Payments\Http\Subscriptions;
use Appwrite\Event\Event;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Appwrite\Event\Event;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
@@ -79,8 +79,7 @@ class Create extends Base
Registry $registryPayments,
Document $project,
Event $queueForEvents
)
{
) {
// Feature flag: block if payments disabled
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -158,42 +157,66 @@ class Create extends Base
$state = new \Appwrite\Payments\Provider\ProviderState((string) $primary, $config, (array) ($config['state'] ?? []));
// Find the fixed plan price (not metered features)
$planPriceId = null;
// Plan prices are stored first in the prices array, followed by feature prices
$priceIds = (array) ($planProviders[$primary]['prices'] ?? []);
$apiKey = (string) ($config['secretKey'] ?? '');
// If no prices in plan providers, return error
if (empty($priceIds)) {
$response->setStatusCode(Response::STATUS_CODE_BAD_REQUEST);
$response->json(['message' => 'Plan has no prices configured for provider: ' . $primary]);
return;
}
// Use the first price ID as the plan price
// Plan prices are created first and stored first in the array (see StripeAdapter::ensurePlan)
$planPriceId = null;
foreach ($priceIds as $priceId) {
// Fetch price details to check metadata
try {
$priceData = $adapter->request($apiKey, 'GET', '/prices/' . $priceId);
if (($priceData['metadata']['type'] ?? '') === 'payments_plan_price') {
$planPriceId = $priceId;
break;
}
} catch (\Throwable $e) {
// Skip invalid prices
continue;
if (!empty($priceId)) {
$planPriceId = $priceId;
break;
}
}
// Create checkout session if we have URLs
if ($planPriceId && $successUrl !== '' && $cancelUrl !== '') {
$checkoutSession = $adapter->createCheckoutSession($payer, [
'priceId' => $planPriceId
], $state, [
'successUrl' => $successUrl,
'cancelUrl' => $cancelUrl
]);
$checkoutUrl = $checkoutSession->url;
if ($planPriceId === null) {
$response->setStatusCode(Response::STATUS_CODE_BAD_REQUEST);
$response->json(['message' => 'Plan has no valid prices configured for provider: ' . $primary]);
return;
}
$subRef = $adapter->ensureSubscription($payer, [
'planId' => $planId,
'planProviders' => $planProviders
], $state);
$providerData = [ (string) $primary => [ 'subscriptionId' => $subRef->externalSubscriptionId ] ];
// Use status from provider if available
$initialStatus = (string) ($subRef->metadata['status'] ?? 'pending');
// Create checkout session if we have a price ID and URLs
if ($planPriceId && $successUrl !== '' && $cancelUrl !== '') {
try {
$checkoutSession = $adapter->createCheckoutSession($payer, [
'priceId' => $planPriceId
], $state, [
'successUrl' => $successUrl,
'cancelUrl' => $cancelUrl
]);
$checkoutUrl = $checkoutSession->url;
} catch (\Throwable $e) {
// Log error but continue with subscription creation
// The subscription can be created without checkout URL for manual payment flows
}
}
// Create or ensure subscription exists in provider
try {
$subRef = $adapter->ensureSubscription($payer, [
'planId' => $planId,
'planProviders' => $planProviders
], $state);
$providerData = [ (string) $primary => [ 'subscriptionId' => $subRef->externalSubscriptionId ] ];
// Use status from provider if available
$initialStatus = (string) ($subRef->metadata['status'] ?? 'pending');
} catch (\Throwable $e) {
$response->setStatusCode(Response::STATUS_CODE_INTERNAL_SERVER_ERROR);
$response->json(['message' => 'Failed to create subscription: ' . $e->getMessage()]);
return;
}
} else {
$response->setStatusCode(Response::STATUS_CODE_BAD_REQUEST);
$response->json(['message' => 'No payment provider configured for this project']);
return;
}
$subscription = new Document([
@@ -233,5 +256,3 @@ class Create extends Base
$response->json($responseData);
}
}
@@ -57,8 +57,7 @@ class Get extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
$sub = $dbForPlatform->findOne('payments_subscriptions', [
Query::equal('projectId', [$project->getId()]),
Query::equal('subscriptionId', [$subscriptionId])
@@ -75,10 +74,10 @@ class Get extends Base
Query::equal('projectId', [$project->getId()]),
Query::equal('planId', [$planId])
]);
if ($plan) $arr['plan'] = $plan->getArrayCopy();
if ($plan) {
$arr['plan'] = $plan->getArrayCopy();
}
}
$response->dynamic(new Document($arr), Response::MODEL_PAYMENT_SUBSCRIPTION);
}
}
@@ -2,11 +2,11 @@
namespace Appwrite\Platform\Modules\Payments\Http\Subscriptions;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -62,8 +62,7 @@ class Resume extends Base
Document $user,
Registry $registryPayments,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -131,5 +130,3 @@ class Resume extends Base
$response->noContent();
}
}
@@ -2,12 +2,12 @@
namespace Appwrite\Platform\Modules\Payments\Http\Subscriptions;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -73,8 +73,7 @@ class Update extends Base
Document $user,
Registry $registryPayments,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -154,5 +153,3 @@ class Update extends Base
$response->json($sub->getArrayCopy());
}
}
@@ -61,12 +61,17 @@ class XList extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
$filters = [ Query::equal('projectId', [$project->getId()]) ];
if ($actorType !== '') $filters[] = Query::equal('actorType', [$actorType]);
if ($actorId !== '') $filters[] = Query::equal('actorId', [$actorId]);
if ($status !== '') $filters[] = Query::equal('status', [$status]);
if ($actorType !== '') {
$filters[] = Query::equal('actorType', [$actorType]);
}
if ($actorId !== '') {
$filters[] = Query::equal('actorId', [$actorId]);
}
if ($status !== '') {
$filters[] = Query::equal('status', [$status]);
}
$list = $dbForPlatform->find('payments_subscriptions', $filters);
$plansById = [];
foreach ($list as $sub) {
@@ -76,7 +81,9 @@ class XList extends Base
Query::equal('projectId', [$project->getId()]),
Query::equal('planId', [$planId])
]);
if ($plan) $plansById[$planId] = $plan;
if ($plan) {
$plansById[$planId] = $plan;
}
}
}
$subs = [];
@@ -91,5 +98,3 @@ class XList extends Base
$response->dynamic(new Document(['total' => count($subs), 'subscriptions' => $subs]), Response::MODEL_PAYMENT_SUBSCRIPTION_LIST);
}
}
@@ -66,8 +66,7 @@ class Create extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
// Feature flag: block if payments disabled for project
$projDoc = $dbForPlatform->getDocument('projects', $project->getId());
$paymentsCfg = (array) $projDoc->getAttribute('payments', []);
@@ -105,5 +104,3 @@ class Create extends Base
$response->json($created->getArrayCopy());
}
}
@@ -62,11 +62,14 @@ class XList extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
$filters = [ Query::equal('projectId', [$project->getId()]) ];
if ($subscriptionId !== '') $filters[] = Query::equal('subscriptionId', [$subscriptionId]);
if ($featureId !== '') $filters[] = Query::equal('featureId', [$featureId]);
if ($subscriptionId !== '') {
$filters[] = Query::equal('subscriptionId', [$subscriptionId]);
}
if ($featureId !== '') {
$filters[] = Query::equal('featureId', [$featureId]);
}
$list = $dbForPlatform->find('payments_usage_events', $filters);
$response->json([
'total' => count($list),
@@ -74,5 +77,3 @@ class XList extends Base
]);
}
}
@@ -60,8 +60,7 @@ class Get extends Base
Response $response,
Database $dbForPlatform,
Document $project
)
{
) {
$events = $dbForPlatform->find('payments_usage_events', [
Query::equal('projectId', [$project->getId()]),
Query::equal('subscriptionId', [$subscriptionId])
@@ -77,5 +76,3 @@ class Get extends Base
]);
}
}
@@ -2,11 +2,11 @@
namespace Appwrite\Platform\Modules\Payments\Http\Usage\Reconcile;
use Appwrite\Payments\Provider\Registry as ProviderRegistry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\Utopia\Response;
use Appwrite\Payments\Provider\Registry as ProviderRegistry;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
@@ -96,5 +96,3 @@ class Create extends Base
$response->noContent();
}
}
@@ -2,12 +2,10 @@
namespace Appwrite\Platform\Modules\Payments\Http\Webhooks\Provider;
use Appwrite\Payments\Provider\Registry;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\Utopia\Response;
use Appwrite\Payments\Provider\Registry;
use Utopia\Database\Document;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
@@ -68,12 +66,14 @@ class Create extends Base
$payload = \file_get_contents('php://input') ?: '';
$signature = (string) ($request->getHeader('stripe-signature') ?? '');
$json = [];
try { $json = \json_decode($payload, true) ?: []; } catch (\Throwable $e) { $json = []; }
try {
$json = \json_decode($payload, true) ?: [];
} catch (\Throwable $e) {
$json = [];
}
$json['_signature'] = $signature;
$json['_raw'] = $payload;
$registryPayments->get($providerId, $config, $project, $dbForPlatform, $dbForProject)->handleWebhook($json , new \Appwrite\Payments\Provider\ProviderState($providerId, $config, (array) ($config['state'] ?? [])));
$registryPayments->get($providerId, $config, $project, $dbForPlatform, $dbForProject)->handleWebhook($json, new \Appwrite\Payments\Provider\ProviderState($providerId, $config, (array) ($config['state'] ?? [])));
$response->noContent();
}
}
@@ -14,5 +14,3 @@ class Module extends Platform\Module
$this->addService('workers', new Workers());
}
}
@@ -2,31 +2,31 @@
namespace Appwrite\Platform\Modules\Payments\Services;
use Appwrite\Platform\Modules\Payments\Http\Plans\Create as PlansCreate;
use Appwrite\Platform\Modules\Payments\Http\Plans\Get as PlansGet;
use Appwrite\Platform\Modules\Payments\Http\Plans\XList as PlansList;
use Appwrite\Platform\Modules\Payments\Http\Plans\Update as PlansUpdate;
use Appwrite\Platform\Modules\Payments\Http\Plans\Delete as PlansDelete;
use Appwrite\Platform\Modules\Payments\Http\Features\Create as FeaturesCreate;
use Appwrite\Platform\Modules\Payments\Http\Features\XList as FeaturesList;
use Appwrite\Platform\Modules\Payments\Http\Features\Update as FeaturesUpdate;
use Appwrite\Platform\Modules\Payments\Http\Features\Delete as FeaturesDelete;
use Appwrite\Platform\Modules\Payments\Http\Features\Update as FeaturesUpdate;
use Appwrite\Platform\Modules\Payments\Http\Features\XList as FeaturesList;
use Appwrite\Platform\Modules\Payments\Http\PlanFeatures\Assign as PlanFeaturesAssign;
use Appwrite\Platform\Modules\Payments\Http\PlanFeatures\XList as PlanFeaturesList;
use Appwrite\Platform\Modules\Payments\Http\PlanFeatures\Remove as PlanFeaturesRemove;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Create as SubscriptionsCreate;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\XList as SubscriptionsList;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Get as SubscriptionsGet;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Update as SubscriptionsUpdate;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Cancel as SubscriptionsCancel;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Resume as SubscriptionsResume;
use Appwrite\Platform\Modules\Payments\Http\Usage\Get as UsageGet;
use Appwrite\Platform\Modules\Payments\Http\Usage\Create as UsageCreate;
use Appwrite\Platform\Modules\Payments\Http\Usage\Events\XList as UsageEventsList;
use Appwrite\Platform\Modules\Payments\Http\Usage\Reconcile\Create as UsageReconcile;
use Appwrite\Platform\Modules\Payments\Http\PlanFeatures\XList as PlanFeaturesList;
use Appwrite\Platform\Modules\Payments\Http\Plans\Create as PlansCreate;
use Appwrite\Platform\Modules\Payments\Http\Plans\Delete as PlansDelete;
use Appwrite\Platform\Modules\Payments\Http\Plans\Get as PlansGet;
use Appwrite\Platform\Modules\Payments\Http\Plans\Update as PlansUpdate;
use Appwrite\Platform\Modules\Payments\Http\Plans\XList as PlansList;
use Appwrite\Platform\Modules\Payments\Http\Providers\Actions\Test\Create as ProvidersTest;
use Appwrite\Platform\Modules\Payments\Http\Providers\Get as ProvidersGet;
use Appwrite\Platform\Modules\Payments\Http\Providers\Update as ProvidersUpdate;
use Appwrite\Platform\Modules\Payments\Http\Providers\Actions\Test\Create as ProvidersTest;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Cancel as SubscriptionsCancel;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Create as SubscriptionsCreate;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Get as SubscriptionsGet;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Resume as SubscriptionsResume;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\Update as SubscriptionsUpdate;
use Appwrite\Platform\Modules\Payments\Http\Subscriptions\XList as SubscriptionsList;
use Appwrite\Platform\Modules\Payments\Http\Usage\Create as UsageCreate;
use Appwrite\Platform\Modules\Payments\Http\Usage\Events\XList as UsageEventsList;
use Appwrite\Platform\Modules\Payments\Http\Usage\Get as UsageGet;
use Appwrite\Platform\Modules\Payments\Http\Usage\Reconcile\Create as UsageReconcile;
use Appwrite\Platform\Modules\Payments\Http\Webhooks\Provider\Create as WebhookProviderCreate;
use Utopia\Platform\Service;
@@ -77,5 +77,3 @@ class Http extends Service
$this->addAction(WebhookProviderCreate::getName(), new WebhookProviderCreate());
}
}
@@ -13,5 +13,3 @@ class Workers extends Service
$this->addAction(UsageSync::getName(), new UsageSync());
}
}
@@ -2,10 +2,10 @@
namespace Appwrite\Platform\Modules\Payments\Workers;
use Appwrite\Payments\Provider\Registry as ProviderRegistry;
use Appwrite\Platform\Action;
use Appwrite\Payments\Provider\ProviderState;
use Appwrite\Payments\Provider\ProviderSubscriptionRef;
use Appwrite\Payments\Provider\Registry as ProviderRegistry;
use Appwrite\Platform\Action;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
@@ -99,5 +99,3 @@ class UsageSync extends Action
}
}
}
+1 -1
View File
@@ -10,6 +10,7 @@ use Appwrite\Platform\Tasks\QueueRetry;
use Appwrite\Platform\Tasks\ScheduleExecutions;
use Appwrite\Platform\Tasks\ScheduleFunctions;
use Appwrite\Platform\Tasks\ScheduleMessages;
use Appwrite\Platform\Tasks\SchedulePaymentsUsage;
use Appwrite\Platform\Tasks\Screenshot;
use Appwrite\Platform\Tasks\SDKs;
use Appwrite\Platform\Tasks\Specs;
@@ -18,7 +19,6 @@ use Appwrite\Platform\Tasks\StatsResources;
use Appwrite\Platform\Tasks\Upgrade;
use Appwrite\Platform\Tasks\Vars;
use Appwrite\Platform\Tasks\Version;
use Appwrite\Platform\Tasks\SchedulePaymentsUsage;
use Utopia\Platform\Service;
class Tasks extends Service
@@ -11,7 +11,6 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Platform\Action;
use Utopia\Queue\Broker\Pool as BrokerPool;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class SchedulePaymentsUsage extends Action
@@ -67,5 +66,3 @@ class SchedulePaymentsUsage extends Action
}, $interval);
}
}
@@ -52,5 +52,3 @@ class PaymentFeature extends Model
return Response::MODEL_PAYMENT_FEATURE;
}
}
@@ -32,5 +32,3 @@ class PaymentPlan extends Model
return Response::MODEL_PAYMENT_PLAN;
}
}
@@ -64,5 +64,3 @@ class PaymentSubscription extends Model
return Response::MODEL_PAYMENT_SUBSCRIPTION;
}
}