UNPKG

quantictech-subscription-components

Version:

Biblioteca de componentes reutilizáveis para sistema de assinatura com Stripe - Arquitetura Service-to-Service

260 lines (259 loc) 16.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var react_1 = require("react"); var paymentApi_1 = require("../utils/paymentApi"); // Função para mapear dados do Stripe para o formato do componente var mapStripeDataToSubscription = function (stripeData) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5; if (!(stripeData === null || stripeData === void 0 ? void 0 : stripeData.id) || !(stripeData === null || stripeData === void 0 ? void 0 : stripeData.status)) return null; try { console.log('🔄 Mapeando dados do Stripe:', { id: stripeData.id, status: stripeData.status, start_date: stripeData.start_date, created: stripeData.created, current_period_end: stripeData.current_period_end, cancel_at: stripeData.cancel_at, canceled_at: stripeData.canceled_at }); // Mapear status do Stripe para o formato do componente var mapStatus = function (stripeStatus) { switch (stripeStatus) { case 'active': return 'active'; case 'canceled': return 'cancelled'; case 'cancelled': return 'cancelled'; case 'incomplete': return 'pending'; case 'incomplete_expired': return 'expired'; case 'past_due': return 'pending'; case 'unpaid': return 'pending'; case 'trialing': return 'active'; default: return 'pending'; } }; // Calcular datas var startDate = stripeData.start_date ? new Date(stripeData.start_date * 1000).toISOString() : stripeData.created ? new Date(stripeData.created * 1000).toISOString() : new Date().toISOString(); var endDate = stripeData.current_period_end ? new Date(stripeData.current_period_end * 1000).toISOString() : ''; var cancelAt = stripeData.cancel_at ? new Date(stripeData.cancel_at * 1000) : undefined; var canceledAt = stripeData.canceled_at ? new Date(stripeData.canceled_at * 1000) : undefined; // Mapear campos do Stripe para o formato esperado pelo componente var mappedSubscription = { _id: stripeData.id, id: stripeData.id, userId: ((_a = stripeData.metadata) === null || _a === void 0 ? void 0 : _a.userId) || '', stripeSubscriptionId: stripeData.id, status: mapStatus(stripeData.status), paymentMethod: 'credit_card', // Datas mapeadas startDate: startDate, endDate: endDate, // Mapear informações do plano planId: { _id: ((_b = stripeData.plan) === null || _b === void 0 ? void 0 : _b.id) || ((_f = (_e = (_d = (_c = stripeData.items) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.price) === null || _f === void 0 ? void 0 : _f.id) || '', name: ((_g = stripeData.plan) === null || _g === void 0 ? void 0 : _g.nickname) || ((_l = (_k = (_j = (_h = stripeData.items) === null || _h === void 0 ? void 0 : _h.data) === null || _j === void 0 ? void 0 : _j[0]) === null || _k === void 0 ? void 0 : _k.price) === null || _l === void 0 ? void 0 : _l.nickname) || 'Plano Premium', description: "Plano ".concat(((_m = stripeData.plan) === null || _m === void 0 ? void 0 : _m.interval) || ((_s = (_r = (_q = (_p = (_o = stripeData.items) === null || _o === void 0 ? void 0 : _o.data) === null || _p === void 0 ? void 0 : _p[0]) === null || _q === void 0 ? void 0 : _q.price) === null || _r === void 0 ? void 0 : _r.recurring) === null || _s === void 0 ? void 0 : _s.interval) || 'mensal'), interval: ((_t = stripeData.plan) === null || _t === void 0 ? void 0 : _t.interval) || ((_y = (_x = (_w = (_v = (_u = stripeData.items) === null || _u === void 0 ? void 0 : _u.data) === null || _v === void 0 ? void 0 : _v[0]) === null || _w === void 0 ? void 0 : _w.price) === null || _x === void 0 ? void 0 : _x.recurring) === null || _y === void 0 ? void 0 : _y.interval) || 'month', price: (((_z = stripeData.plan) === null || _z === void 0 ? void 0 : _z.amount) || ((_3 = (_2 = (_1 = (_0 = stripeData.items) === null || _0 === void 0 ? void 0 : _0.data) === null || _1 === void 0 ? void 0 : _1[0]) === null || _2 === void 0 ? void 0 : _2.price) === null || _3 === void 0 ? void 0 : _3.unit_amount) || 0) / 100, currency: stripeData.currency || 'brl', features: [] }, // Mapear informações do cliente dos metadados customerInfo: stripeData.metadata ? { email: stripeData.metadata.customerEmail || '', firstName: ((_4 = stripeData.metadata.customerName) === null || _4 === void 0 ? void 0 : _4.split(' ')[0]) || '', lastName: ((_5 = stripeData.metadata.customerName) === null || _5 === void 0 ? void 0 : _5.split(' ').slice(1).join(' ')) || '', document: stripeData.metadata.cpf || '', documentType: 'cpf' } : undefined, // Datas de cancelamento se existirem cancelAt: cancelAt, canceledAt: canceledAt, createdAt: stripeData.created ? new Date(stripeData.created * 1000).toISOString() : undefined, currentPeriodEnd: stripeData.current_period_end ? new Date(stripeData.current_period_end * 1000).toISOString() : undefined }; console.log('✅ Subscription mapeada com sucesso:', mappedSubscription); return mappedSubscription; } catch (error) { console.error('Erro ao mapear dados do Stripe:', error); return null; } }; var useSubscription = function () { var _a = (0, react_1.useState)(null), subscription = _a[0], setSubscription = _a[1]; var _b = (0, react_1.useState)(true), loading = _b[0], setLoading = _b[1]; var _c = (0, react_1.useState)(null), error = _c[0], setError = _c[1]; // Função utilitária para verificação automática de status var performAutomaticStatusCheck = function (currentSubscription) { return __awaiter(void 0, void 0, void 0, function () { var syncKey, lastCheck, now, oneHour, canceledKey, wasCanceledLocally, verificationResult, response, error_1, canceledKey, stripeSubscription, mappedSubscription, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(currentSubscription === null || currentSubscription === void 0 ? void 0 : currentSubscription.stripeSubscriptionId)) { return [2 /*return*/, currentSubscription]; } syncKey = "sync_status_".concat(currentSubscription.stripeSubscriptionId); lastCheck = localStorage.getItem(syncKey); now = Date.now(); oneHour = 60 * 60 * 1000; // Se já verificamos recentemente, pular if (lastCheck && (now - parseInt(lastCheck)) < oneHour) { console.log('🛡️ Proteção contra loop: Verificação recente encontrada, pulando'); canceledKey = "canceled_locally_".concat(currentSubscription.stripeSubscriptionId); wasCanceledLocally = localStorage.getItem(canceledKey); if (wasCanceledLocally) { console.log('✅ Assinatura já foi cancelada localmente, removendo da interface'); return [2 /*return*/, null]; } return [2 /*return*/, currentSubscription]; } _a.label = 1; case 1: _a.trys.push([1, 11, , 12]); console.log('🔄 Verificando status automaticamente no Stripe...'); // Marcar que estamos verificando agora localStorage.setItem(syncKey, now.toString()); return [4 /*yield*/, (0, paymentApi_1.verifySubscriptionStatusWithStripe)(currentSubscription.stripeSubscriptionId)]; case 2: verificationResult = _a.sent(); if (!(verificationResult.success && verificationResult.needsSync)) return [3 /*break*/, 9]; console.log('⚠️ Inconsistência detectada! Assinatura cancelada no Stripe mas ativa no sistema'); if (!verificationResult.wasSynced) return [3 /*break*/, 7]; console.log('✅ Sincronização automática bem-sucedida'); _a.label = 3; case 3: _a.trys.push([3, 5, , 6]); return [4 /*yield*/, paymentApi_1.apiEndpoints.getUserSubscription()]; case 4: response = _a.sent(); if (response.data && Array.isArray(response.data) && response.data.length > 0) { return [2 /*return*/, response.data[0]]; } else { return [2 /*return*/, null]; } return [3 /*break*/, 6]; case 5: error_1 = _a.sent(); console.error('Erro ao recarregar dados da assinatura:', error_1); return [2 /*return*/, null]; case 6: return [3 /*break*/, 8]; case 7: // Se a sincronização falhou, mas sabemos que foi cancelada no Stripe console.log('⚠️ Sincronização com backend falhou, mas assinatura foi cancelada no Stripe'); canceledKey = "canceled_locally_".concat(currentSubscription.stripeSubscriptionId); localStorage.setItem(canceledKey, now.toString()); return [2 /*return*/, null]; case 8: return [3 /*break*/, 10]; case 9: if (verificationResult.success && verificationResult.subscription) { // Se obtivemos dados atualizados do Stripe, mapear e usar console.log('🔄 Dados atualizados obtidos do Stripe, mapeando para formato do componente...'); stripeSubscription = verificationResult.subscription; if ((stripeSubscription === null || stripeSubscription === void 0 ? void 0 : stripeSubscription.id) && (stripeSubscription === null || stripeSubscription === void 0 ? void 0 : stripeSubscription.status)) { mappedSubscription = mapStripeDataToSubscription(stripeSubscription); if (mappedSubscription) { console.log('✅ Dados do Stripe mapeados com sucesso'); return [2 /*return*/, mappedSubscription]; } } } _a.label = 10; case 10: return [2 /*return*/, currentSubscription]; case 11: error_2 = _a.sent(); console.error('Erro na verificação automática de status:', error_2); localStorage.setItem(syncKey, now.toString()); return [2 /*return*/, currentSubscription]; case 12: return [2 /*return*/]; } }); }); }; var fetchSubscription = function () { return __awaiter(void 0, void 0, void 0, function () { var subscriptionResponse, currentSubscription, finalSubscription, error_3; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, 4, 5]); setLoading(true); setError(null); return [4 /*yield*/, paymentApi_1.apiEndpoints.getUserSubscription()]; case 1: subscriptionResponse = _a.sent(); currentSubscription = Array.isArray(subscriptionResponse.data) ? subscriptionResponse.data[0] : subscriptionResponse.data; return [4 /*yield*/, performAutomaticStatusCheck(currentSubscription)]; case 2: finalSubscription = _a.sent(); setSubscription(finalSubscription); return [3 /*break*/, 5]; case 3: error_3 = _a.sent(); console.error('❌ Erro ao buscar dados de assinatura:', error_3); setError('Não foi possível carregar os dados da assinatura.'); return [3 /*break*/, 5]; case 4: setLoading(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }; var clearSubscription = function () { setSubscription(null); }; (0, react_1.useEffect)(function () { fetchSubscription(); }, []); return { subscription: subscription, loading: loading, error: error, refetch: fetchSubscription, clearSubscription: clearSubscription }; }; exports.default = useSubscription;