mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
progress
This commit is contained in:
@@ -130,5 +130,3 @@ return [
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user