feat: revenue cat
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table): void {
|
||||
$table->string('subscription_product_id')->nullable()->index();
|
||||
$table->string('subscription_store')->nullable();
|
||||
$table->timestampTz('subscription_expires_at')->nullable()->index();
|
||||
$table->boolean('subscription_is_trial')->default(false);
|
||||
});
|
||||
|
||||
Schema::create('revenue_cat_webhook_events', function (Blueprint $table): void {
|
||||
$table->ulid('id')->primary();
|
||||
$table->string('event_id')->unique();
|
||||
$table->string('type')->index();
|
||||
$table->string('app_user_id')->nullable()->index();
|
||||
$table->string('product_id')->nullable();
|
||||
$table->json('entitlement_ids')->nullable();
|
||||
$table->string('environment')->nullable();
|
||||
$table->json('payload');
|
||||
$table->timestampTz('processed_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::dropIfExists('subscription_items');
|
||||
Schema::dropIfExists('subscriptions');
|
||||
Schema::dropIfExists('payment_transactions');
|
||||
Schema::dropIfExists('billing_products');
|
||||
|
||||
Schema::table('users', function (Blueprint $table): void {
|
||||
$table->dropIndex(['stripe_id']);
|
||||
$table->dropColumn([
|
||||
'stripe_id',
|
||||
'pm_type',
|
||||
'pm_last_four',
|
||||
'trial_ends_at',
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('revenue_cat_webhook_events');
|
||||
|
||||
Schema::table('users', function (Blueprint $table): void {
|
||||
$table->dropIndex(['subscription_product_id']);
|
||||
$table->dropIndex(['subscription_expires_at']);
|
||||
$table->dropColumn([
|
||||
'subscription_product_id',
|
||||
'subscription_store',
|
||||
'subscription_expires_at',
|
||||
'subscription_is_trial',
|
||||
]);
|
||||
$table->string('stripe_id')->nullable()->index();
|
||||
$table->string('pm_type')->nullable();
|
||||
$table->string('pm_last_four', 4)->nullable();
|
||||
$table->timestamp('trial_ends_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('billing_products', function (Blueprint $table): void {
|
||||
$table->ulid('id')->primary();
|
||||
$table->string('name');
|
||||
$table->text('description')->nullable();
|
||||
$table->string('purpose', 32)->unique();
|
||||
$table->unsignedInteger('amount');
|
||||
$table->string('currency', 3)->default('eur');
|
||||
$table->string('stripe_product_id')->nullable()->index();
|
||||
$table->string('stripe_price_id')->nullable()->unique();
|
||||
$table->boolean('is_active')->default(true)->index();
|
||||
$table->unsignedInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
$table->index(['purpose', 'is_active', 'sort_order']);
|
||||
});
|
||||
|
||||
Schema::create('payment_transactions', function (Blueprint $table): void {
|
||||
$table->ulid('id')->primary();
|
||||
$table->foreignUlid('user_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->foreignUlid('billing_product_id')->nullable()->constrained('billing_products')->nullOnDelete();
|
||||
$table->string('type', 64);
|
||||
$table->string('status', 64);
|
||||
$table->string('stripe_event_id')->nullable()->index();
|
||||
$table->string('stripe_event_type')->nullable()->index();
|
||||
$table->string('stripe_customer_id')->nullable()->index();
|
||||
$table->string('stripe_checkout_session_id')->nullable()->index();
|
||||
$table->string('stripe_invoice_id')->nullable()->index();
|
||||
$table->string('stripe_payment_intent_id')->nullable()->index();
|
||||
$table->string('stripe_subscription_id')->nullable()->index();
|
||||
$table->string('stripe_price_id')->nullable()->index();
|
||||
$table->unsignedInteger('amount')->nullable();
|
||||
$table->string('currency', 3)->nullable();
|
||||
$table->string('invoice_url', 2048)->nullable();
|
||||
$table->string('invoice_pdf_url', 2048)->nullable();
|
||||
$table->timestamp('invoice_email_sent_at')->nullable();
|
||||
$table->text('error_message')->nullable();
|
||||
$table->json('payload')->nullable();
|
||||
$table->timestamp('processed_at')->nullable();
|
||||
$table->timestamps();
|
||||
$table->index(['status', 'created_at']);
|
||||
$table->index(['type', 'status', 'created_at']);
|
||||
});
|
||||
|
||||
Schema::create('subscriptions', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->foreignUlid('user_id');
|
||||
$table->string('type');
|
||||
$table->string('stripe_id')->unique();
|
||||
$table->string('stripe_status');
|
||||
$table->string('stripe_price')->nullable();
|
||||
$table->integer('quantity')->nullable();
|
||||
$table->timestamp('trial_ends_at')->nullable();
|
||||
$table->timestamp('ends_at')->nullable();
|
||||
$table->timestamps();
|
||||
$table->index(['user_id', 'stripe_status']);
|
||||
});
|
||||
|
||||
Schema::create('subscription_items', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->foreignId('subscription_id');
|
||||
$table->string('stripe_id')->unique();
|
||||
$table->string('stripe_product');
|
||||
$table->string('stripe_price');
|
||||
$table->string('meter_id')->nullable();
|
||||
$table->integer('quantity')->nullable();
|
||||
$table->string('meter_event_name')->nullable();
|
||||
$table->timestamps();
|
||||
$table->index(['subscription_id', 'stripe_price']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -64,7 +64,7 @@ class LegalDocumentSeeder extends Seeder
|
||||
[
|
||||
'title' => 'Paiements et abonnements',
|
||||
'body' => [
|
||||
'Stripe traite les paiements, les abonnements, les factures et les informations de moyen de paiement. Bowli conserve uniquement les identifiants et statuts nécessaires au suivi de la transaction et de tes droits d’accès.',
|
||||
'Apple ou Google traite le paiement selon ton appareil. RevenueCat synchronise les achats, abonnements et droits d’accès sans recevoir les informations complètes de ton moyen de paiement.',
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -118,8 +118,8 @@ class LegalDocumentSeeder extends Seeder
|
||||
[
|
||||
'title' => 'Essai, abonnement et certification',
|
||||
'body' => [
|
||||
'La publication de plats reste accessible sans abonnement. L’analyse automatique des photos est accessible pendant la période d’essai indiquée dans l’application, puis avec l’unique formule d’abonnement active.',
|
||||
'La certification est un achat unique distinct de l’abonnement. Le paiement donne le droit de soumettre une demande, mais le badge n’est accordé qu’après vérification d’une pièce d’identité valide. En cas de refus, une nouvelle pièce peut être envoyée sans nouvel achat.',
|
||||
'La publication de plats reste accessible sans abonnement. L’analyse automatique des photos est accessible avec un abonnement mensuel ou annuel. Une éventuelle période d’essai concerne uniquement ces abonnements et sa durée est affichée avant validation par le store.',
|
||||
'La certification ne comporte aucune période d’essai. C’est un achat unique distinct de l’abonnement : le paiement donne le droit de soumettre une demande, mais le badge n’est accordé qu’après vérification d’une pièce d’identité valide. En cas de refus, une nouvelle pièce peut être envoyée sans nouvel achat.',
|
||||
],
|
||||
],
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user