From 7e4a5350383f1e7179ecb80a2036c1fd1b890256 Mon Sep 17 00:00:00 2001 From: DANYDHSV Date: Tue, 23 Dec 2025 14:50:01 -0600 Subject: [PATCH] =?UTF-8?q?feat(stripe-sync):=20implementar=20resoluci?= =?UTF-8?q?=C3=B3n=20din=C3=A1mica=20de=20m=C3=A9todos=20de=20pago=20y=20c?= =?UTF-8?q?orregir=20validaci=C3=B3n=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Se agregó 'getPaymentMethodIdByName' para buscar automáticamente el ID de "Transferencia bancaria" por nombre, asegurando portabilidad entre servidores UISP. - Se implementó el manejo del webhook 'customer_cash_balance_transaction.created' para el registro automático de pagos fondeados. - Fix: Se corrigió error 422 en la API de UCRM forzando el cast de 'clientId' a integer y 'methodId' a string (GUID). - Se actualizó la documentación (README/CHANGELOG) con instrucciones de configuración de webhooks. --- .DS_Store | Bin .gitignore | 4 +- CHANGELOG.md | 8 + PaymentIntentStripe.php | 635 ++++++ README.md | 2 +- archivos_webhook_destino/main.php | 23 + archivos_webhook_destino/public.php | 147 ++ .../Facade/AbstractMessageNotifierFacade.php | 1873 +++++++++++++++++ .../Facade/AbstractOxxoOperationsFacade.php | 738 +++++++ .../Facade/AbstractStripeOperationsFacade.php | 489 +++++ .../src/Facade/AbstractUpdateClientFacade.php | 98 + .../src/Facade/ClientCallBellAPI.php | 1346 ++++++++++++ .../src/Facade/PluginNotifierFacade.php | 58 + .../src/Facade/PluginOxxoNotifierFacade.php | 58 + .../src/Facade/TwilioNotifierFacade.php | 93 + .../src/Facade/pruebas_ucrm_api.php | 5 + archivos_webhook_destino/src/Plugin.php | 699 ++++++ composer.json | 0 composer.lock | 0 data/config.json | 2 +- data/plugin.log | 20 + img/vista-general.png | Bin main.php | 0 manifest.json | 0 public.php | 146 +- src/PaymentIntentService.php | 73 +- ucrm.json | 0 vendor/.DS_Store | Bin vendor/autoload.php | 0 vendor/bin/pack-plugin | 0 vendor/composer/ClassLoader.php | 0 vendor/composer/InstalledVersions.php | 0 vendor/composer/LICENSE | 0 vendor/composer/autoload_classmap.php | 467 ++++ vendor/composer/autoload_files.php | 0 vendor/composer/autoload_namespaces.php | 0 vendor/composer/autoload_psr4.php | 0 vendor/composer/autoload_real.php | 11 +- vendor/composer/autoload_static.php | 467 ++++ vendor/composer/installed.json | 2 +- vendor/composer/installed.php | 14 +- vendor/composer/platform_check.php | 0 vendor/guzzlehttp/.DS_Store | Bin vendor/guzzlehttp/guzzle/CHANGELOG 2.md | 0 vendor/guzzlehttp/guzzle/CHANGELOG.md | 0 vendor/guzzlehttp/guzzle/LICENSE | 0 vendor/guzzlehttp/guzzle/LICENSE 2 | 0 vendor/guzzlehttp/guzzle/README 2.md | 0 vendor/guzzlehttp/guzzle/README.md | 0 vendor/guzzlehttp/guzzle/UPGRADING 2.md | 0 vendor/guzzlehttp/guzzle/UPGRADING.md | 0 vendor/guzzlehttp/guzzle/composer 2.json | 0 vendor/guzzlehttp/guzzle/composer.json | 0 vendor/guzzlehttp/guzzle/package-lock.json | 0 .../guzzlehttp/guzzle/src/BodySummarizer.php | 0 .../guzzle/src/BodySummarizerInterface.php | 0 vendor/guzzlehttp/guzzle/src/Client.php | 0 .../guzzlehttp/guzzle/src/ClientInterface.php | 0 vendor/guzzlehttp/guzzle/src/ClientTrait.php | 0 .../guzzle/src/Cookie/CookieJar.php | 0 .../guzzle/src/Cookie/CookieJarInterface.php | 0 .../guzzle/src/Cookie/FileCookieJar.php | 0 .../guzzle/src/Cookie/SessionCookieJar.php | 0 .../guzzle/src/Cookie/SetCookie.php | 0 .../src/Exception/BadResponseException.php | 0 .../guzzle/src/Exception/ClientException.php | 0 .../guzzle/src/Exception/ConnectException.php | 0 .../guzzle/src/Exception/GuzzleException.php | 0 .../Exception/InvalidArgumentException.php | 0 .../guzzle/src/Exception/RequestException.php | 0 .../guzzle/src/Exception/ServerException.php | 0 .../Exception/TooManyRedirectsException.php | 0 .../src/Exception/TransferException.php | 0 .../guzzle/src/Handler/CurlFactory.php | 0 .../src/Handler/CurlFactoryInterface.php | 0 .../guzzle/src/Handler/CurlHandler.php | 0 .../guzzle/src/Handler/CurlMultiHandler.php | 0 .../guzzle/src/Handler/EasyHandle.php | 0 .../guzzle/src/Handler/HeaderProcessor.php | 0 .../guzzle/src/Handler/MockHandler.php | 0 .../guzzlehttp/guzzle/src/Handler/Proxy.php | 0 .../guzzle/src/Handler/StreamHandler.php | 0 vendor/guzzlehttp/guzzle/src/HandlerStack.php | 0 .../guzzle/src/MessageFormatter.php | 0 .../guzzle/src/MessageFormatterInterface.php | 0 vendor/guzzlehttp/guzzle/src/Middleware.php | 0 vendor/guzzlehttp/guzzle/src/Pool.php | 0 .../guzzle/src/PrepareBodyMiddleware.php | 0 .../guzzle/src/RedirectMiddleware.php | 0 .../guzzlehttp/guzzle/src/RequestOptions.php | 0 .../guzzlehttp/guzzle/src/RetryMiddleware.php | 0 .../guzzlehttp/guzzle/src/TransferStats.php | 0 vendor/guzzlehttp/guzzle/src/Utils.php | 0 vendor/guzzlehttp/guzzle/src/functions.php | 0 .../guzzle/src/functions_include.php | 0 vendor/guzzlehttp/promises/CHANGELOG 2.md | 0 vendor/guzzlehttp/promises/CHANGELOG.md | 0 vendor/guzzlehttp/promises/LICENSE | 0 vendor/guzzlehttp/promises/LICENSE 2 | 0 vendor/guzzlehttp/promises/README 2.md | 0 vendor/guzzlehttp/promises/README.md | 0 vendor/guzzlehttp/promises/composer 2.json | 0 vendor/guzzlehttp/promises/composer.json | 0 .../promises/src/AggregateException.php | 0 .../promises/src/CancellationException.php | 0 vendor/guzzlehttp/promises/src/Coroutine.php | 0 vendor/guzzlehttp/promises/src/Create.php | 0 vendor/guzzlehttp/promises/src/Each.php | 0 .../guzzlehttp/promises/src/EachPromise.php | 0 .../promises/src/FulfilledPromise.php | 0 vendor/guzzlehttp/promises/src/Is.php | 0 vendor/guzzlehttp/promises/src/Promise.php | 0 .../promises/src/PromiseInterface.php | 0 .../promises/src/PromisorInterface.php | 0 .../promises/src/RejectedPromise.php | 0 .../promises/src/RejectionException.php | 0 vendor/guzzlehttp/promises/src/TaskQueue.php | 0 .../promises/src/TaskQueueInterface.php | 0 vendor/guzzlehttp/promises/src/Utils.php | 0 vendor/guzzlehttp/psr7/CHANGELOG.md | 0 vendor/guzzlehttp/psr7/LICENSE | 0 vendor/guzzlehttp/psr7/README.md | 0 vendor/guzzlehttp/psr7/composer.json | 0 vendor/guzzlehttp/psr7/src/AppendStream.php | 0 vendor/guzzlehttp/psr7/src/BufferStream.php | 0 vendor/guzzlehttp/psr7/src/CachingStream.php | 0 vendor/guzzlehttp/psr7/src/DroppingStream.php | 0 .../src/Exception/MalformedUriException.php | 0 vendor/guzzlehttp/psr7/src/FnStream.php | 0 vendor/guzzlehttp/psr7/src/Header.php | 0 vendor/guzzlehttp/psr7/src/HttpFactory.php | 0 vendor/guzzlehttp/psr7/src/InflateStream.php | 0 vendor/guzzlehttp/psr7/src/LazyOpenStream.php | 0 vendor/guzzlehttp/psr7/src/LimitStream.php | 0 vendor/guzzlehttp/psr7/src/Message.php | 0 vendor/guzzlehttp/psr7/src/MessageTrait.php | 0 vendor/guzzlehttp/psr7/src/MimeType.php | 0 .../guzzlehttp/psr7/src/MultipartStream.php | 0 vendor/guzzlehttp/psr7/src/NoSeekStream.php | 0 vendor/guzzlehttp/psr7/src/PumpStream.php | 0 vendor/guzzlehttp/psr7/src/Query.php | 0 vendor/guzzlehttp/psr7/src/Request.php | 0 vendor/guzzlehttp/psr7/src/Response.php | 0 vendor/guzzlehttp/psr7/src/Rfc7230.php | 0 vendor/guzzlehttp/psr7/src/ServerRequest.php | 0 vendor/guzzlehttp/psr7/src/Stream.php | 0 .../psr7/src/StreamDecoratorTrait.php | 0 vendor/guzzlehttp/psr7/src/StreamWrapper.php | 0 vendor/guzzlehttp/psr7/src/UploadedFile.php | 0 vendor/guzzlehttp/psr7/src/Uri.php | 0 vendor/guzzlehttp/psr7/src/UriComparator.php | 0 vendor/guzzlehttp/psr7/src/UriNormalizer.php | 0 vendor/guzzlehttp/psr7/src/UriResolver.php | 0 vendor/guzzlehttp/psr7/src/Utils.php | 0 vendor/psr/.DS_Store | Bin vendor/psr/http-client/CHANGELOG.md | 0 vendor/psr/http-client/LICENSE | 0 vendor/psr/http-client/README.md | 0 vendor/psr/http-client/composer.json | 0 .../src/ClientExceptionInterface.php | 0 .../psr/http-client/src/ClientInterface.php | 0 .../src/NetworkExceptionInterface.php | 0 .../src/RequestExceptionInterface.php | 0 vendor/psr/http-factory/LICENSE | 0 vendor/psr/http-factory/LICENSE 2 | 0 vendor/psr/http-factory/README 2.md | 0 vendor/psr/http-factory/README.md | 0 vendor/psr/http-factory/composer 2.json | 0 vendor/psr/http-factory/composer.json | 0 .../src/RequestFactoryInterface.php | 0 .../src/ResponseFactoryInterface.php | 0 .../src/ServerRequestFactoryInterface.php | 0 .../src/StreamFactoryInterface.php | 0 .../src/UploadedFileFactoryInterface.php | 0 .../http-factory/src/UriFactoryInterface.php | 0 vendor/psr/http-message/CHANGELOG.md | 0 vendor/psr/http-message/LICENSE | 0 vendor/psr/http-message/README.md | 0 vendor/psr/http-message/composer.json | 0 .../psr/http-message/docs/PSR7-Interfaces.md | 0 vendor/psr/http-message/docs/PSR7-Usage.md | 0 .../psr/http-message/src/MessageInterface.php | 0 .../psr/http-message/src/RequestInterface.php | 0 .../http-message/src/ResponseInterface.php | 0 .../src/ServerRequestInterface.php | 0 .../psr/http-message/src/StreamInterface.php | 0 .../src/UploadedFileInterface.php | 0 vendor/psr/http-message/src/UriInterface.php | 0 vendor/ralouphie/getallheaders/LICENSE | 0 vendor/ralouphie/getallheaders/README.md | 0 vendor/ralouphie/getallheaders/composer.json | 0 .../getallheaders/src/getallheaders.php | 0 vendor/stripe/stripe-php/.gitignore | 0 vendor/stripe/stripe-php/CHANGELOG.md | 0 vendor/stripe/stripe-php/LICENSE | 0 vendor/stripe/stripe-php/OPENAPI_VERSION | 0 vendor/stripe/stripe-php/README.md | 0 vendor/stripe/stripe-php/VERSION | 0 vendor/stripe/stripe-php/composer.json | 0 .../stripe-php/data/ca-certificates.crt | 0 vendor/stripe/stripe-php/init.php | 0 vendor/stripe/stripe-php/lib/Account.php | 0 vendor/stripe/stripe-php/lib/AccountLink.php | 0 .../stripe/stripe-php/lib/AccountSession.php | 0 .../stripe-php/lib/ApiOperations/All.php | 0 .../stripe-php/lib/ApiOperations/Create.php | 0 .../stripe-php/lib/ApiOperations/Delete.php | 0 .../lib/ApiOperations/NestedResource.php | 0 .../stripe-php/lib/ApiOperations/Request.php | 0 .../stripe-php/lib/ApiOperations/Retrieve.php | 0 .../stripe-php/lib/ApiOperations/Search.php | 0 .../lib/ApiOperations/SingletonRetrieve.php | 0 .../stripe-php/lib/ApiOperations/Update.php | 0 vendor/stripe/stripe-php/lib/ApiRequestor.php | 0 vendor/stripe/stripe-php/lib/ApiResource.php | 0 vendor/stripe/stripe-php/lib/ApiResponse.php | 0 .../stripe/stripe-php/lib/ApplePayDomain.php | 0 vendor/stripe/stripe-php/lib/Application.php | 0 .../stripe/stripe-php/lib/ApplicationFee.php | 0 .../stripe-php/lib/ApplicationFeeRefund.php | 0 vendor/stripe/stripe-php/lib/Apps/Secret.php | 0 vendor/stripe/stripe-php/lib/Balance.php | 0 .../stripe-php/lib/BalanceTransaction.php | 0 vendor/stripe/stripe-php/lib/BankAccount.php | 0 .../stripe-php/lib/BaseStripeClient.php | 0 .../lib/BaseStripeClientInterface.php | 0 .../stripe/stripe-php/lib/Billing/Meter.php | 0 .../stripe-php/lib/Billing/MeterEvent.php | 0 .../lib/Billing/MeterEventAdjustment.php | 0 .../lib/Billing/MeterEventSummary.php | 0 .../lib/BillingPortal/Configuration.php | 0 .../stripe-php/lib/BillingPortal/Session.php | 0 vendor/stripe/stripe-php/lib/Capability.php | 0 vendor/stripe/stripe-php/lib/Card.php | 0 vendor/stripe/stripe-php/lib/CashBalance.php | 0 vendor/stripe/stripe-php/lib/Charge.php | 0 .../stripe-php/lib/Checkout/Session.php | 0 .../stripe/stripe-php/lib/Climate/Order.php | 0 .../stripe/stripe-php/lib/Climate/Product.php | 0 .../stripe-php/lib/Climate/Supplier.php | 0 vendor/stripe/stripe-php/lib/Collection.php | 0 .../stripe-php/lib/ConfirmationToken.php | 0 .../lib/ConnectCollectionTransfer.php | 0 vendor/stripe/stripe-php/lib/CountrySpec.php | 0 vendor/stripe/stripe-php/lib/Coupon.php | 0 vendor/stripe/stripe-php/lib/CreditNote.php | 0 .../stripe-php/lib/CreditNoteLineItem.php | 0 vendor/stripe/stripe-php/lib/Customer.php | 0 .../lib/CustomerBalanceTransaction.php | 0 .../lib/CustomerCashBalanceTransaction.php | 0 .../stripe/stripe-php/lib/CustomerSession.php | 0 vendor/stripe/stripe-php/lib/Discount.php | 0 vendor/stripe/stripe-php/lib/Dispute.php | 0 .../lib/Entitlements/ActiveEntitlement.php | 0 .../stripe-php/lib/Entitlements/Feature.php | 0 vendor/stripe/stripe-php/lib/EphemeralKey.php | 0 vendor/stripe/stripe-php/lib/ErrorObject.php | 0 vendor/stripe/stripe-php/lib/Event.php | 0 .../lib/Exception/ApiConnectionException.php | 0 .../lib/Exception/ApiErrorException.php | 0 .../lib/Exception/AuthenticationException.php | 0 .../lib/Exception/BadMethodCallException.php | 0 .../lib/Exception/CardException.php | 0 .../lib/Exception/ExceptionInterface.php | 0 .../lib/Exception/IdempotencyException.php | 0 .../Exception/InvalidArgumentException.php | 0 .../lib/Exception/InvalidRequestException.php | 0 .../Exception/OAuth/ExceptionInterface.php | 0 .../OAuth/InvalidClientException.php | 0 .../Exception/OAuth/InvalidGrantException.php | 0 .../OAuth/InvalidRequestException.php | 0 .../Exception/OAuth/InvalidScopeException.php | 0 .../Exception/OAuth/OAuthErrorException.php | 0 .../OAuth/UnknownOAuthErrorException.php | 0 .../OAuth/UnsupportedGrantTypeException.php | 0 .../UnsupportedResponseTypeException.php | 0 .../lib/Exception/PermissionException.php | 0 .../lib/Exception/RateLimitException.php | 0 .../SignatureVerificationException.php | 0 .../Exception/UnexpectedValueException.php | 0 .../Exception/UnknownApiErrorException.php | 0 vendor/stripe/stripe-php/lib/ExchangeRate.php | 0 vendor/stripe/stripe-php/lib/File.php | 0 vendor/stripe/stripe-php/lib/FileLink.php | 0 .../lib/FinancialConnections/Account.php | 0 .../lib/FinancialConnections/AccountOwner.php | 0 .../FinancialConnections/AccountOwnership.php | 0 .../lib/FinancialConnections/Session.php | 0 .../lib/FinancialConnections/Transaction.php | 0 .../stripe-php/lib/Forwarding/Request.php | 0 .../stripe-php/lib/FundingInstructions.php | 0 .../lib/HttpClient/ClientInterface.php | 0 .../stripe-php/lib/HttpClient/CurlClient.php | 0 .../HttpClient/StreamingClientInterface.php | 0 .../lib/Identity/VerificationReport.php | 0 .../lib/Identity/VerificationSession.php | 0 vendor/stripe/stripe-php/lib/Invoice.php | 0 vendor/stripe/stripe-php/lib/InvoiceItem.php | 0 .../stripe/stripe-php/lib/InvoiceLineItem.php | 0 .../stripe-php/lib/Issuing/Authorization.php | 0 vendor/stripe/stripe-php/lib/Issuing/Card.php | 0 .../stripe-php/lib/Issuing/CardDetails.php | 0 .../stripe-php/lib/Issuing/Cardholder.php | 0 .../stripe/stripe-php/lib/Issuing/Dispute.php | 0 .../lib/Issuing/PersonalizationDesign.php | 0 .../stripe-php/lib/Issuing/PhysicalBundle.php | 0 .../stripe/stripe-php/lib/Issuing/Token.php | 0 .../stripe-php/lib/Issuing/Transaction.php | 0 vendor/stripe/stripe-php/lib/LineItem.php | 0 vendor/stripe/stripe-php/lib/LoginLink.php | 0 vendor/stripe/stripe-php/lib/Mandate.php | 0 vendor/stripe/stripe-php/lib/OAuth.php | 0 .../stripe-php/lib/OAuthErrorObject.php | 0 .../stripe/stripe-php/lib/PaymentIntent.php | 0 vendor/stripe/stripe-php/lib/PaymentLink.php | 0 .../stripe/stripe-php/lib/PaymentMethod.php | 0 .../lib/PaymentMethodConfiguration.php | 0 .../stripe-php/lib/PaymentMethodDomain.php | 0 vendor/stripe/stripe-php/lib/Payout.php | 0 vendor/stripe/stripe-php/lib/Person.php | 0 vendor/stripe/stripe-php/lib/Plan.php | 0 .../stripe/stripe-php/lib/PlatformTaxFee.php | 0 vendor/stripe/stripe-php/lib/Price.php | 0 vendor/stripe/stripe-php/lib/Product.php | 0 .../stripe/stripe-php/lib/ProductFeature.php | 0 .../stripe/stripe-php/lib/PromotionCode.php | 0 vendor/stripe/stripe-php/lib/Quote.php | 0 .../lib/Radar/EarlyFraudWarning.php | 0 .../stripe/stripe-php/lib/Radar/ValueList.php | 0 .../stripe-php/lib/Radar/ValueListItem.php | 0 .../stripe-php/lib/RecipientTransfer.php | 0 vendor/stripe/stripe-php/lib/Refund.php | 0 .../stripe-php/lib/Reporting/ReportRun.php | 0 .../stripe-php/lib/Reporting/ReportType.php | 0 .../stripe-php/lib/RequestTelemetry.php | 0 .../stripe-php/lib/ReserveTransaction.php | 0 vendor/stripe/stripe-php/lib/Review.php | 0 vendor/stripe/stripe-php/lib/SearchResult.php | 0 .../lib/Service/AbstractService.php | 0 .../lib/Service/AbstractServiceFactory.php | 0 .../lib/Service/AccountLinkService.php | 0 .../stripe-php/lib/Service/AccountService.php | 0 .../lib/Service/AccountSessionService.php | 0 .../lib/Service/ApplePayDomainService.php | 0 .../lib/Service/ApplicationFeeService.php | 0 .../lib/Service/Apps/AppsServiceFactory.php | 0 .../lib/Service/Apps/SecretService.php | 0 .../stripe-php/lib/Service/BalanceService.php | 0 .../lib/Service/BalanceTransactionService.php | 0 .../Service/Billing/BillingServiceFactory.php | 0 .../Billing/MeterEventAdjustmentService.php | 0 .../lib/Service/Billing/MeterEventService.php | 0 .../lib/Service/Billing/MeterService.php | 0 .../BillingPortalServiceFactory.php | 0 .../BillingPortal/ConfigurationService.php | 0 .../Service/BillingPortal/SessionService.php | 0 .../stripe-php/lib/Service/ChargeService.php | 0 .../Checkout/CheckoutServiceFactory.php | 0 .../lib/Service/Checkout/SessionService.php | 0 .../Service/Climate/ClimateServiceFactory.php | 0 .../lib/Service/Climate/OrderService.php | 0 .../lib/Service/Climate/ProductService.php | 0 .../lib/Service/Climate/SupplierService.php | 0 .../lib/Service/ConfirmationTokenService.php | 0 .../lib/Service/CoreServiceFactory.php | 0 .../lib/Service/CountrySpecService.php | 0 .../stripe-php/lib/Service/CouponService.php | 0 .../lib/Service/CreditNoteService.php | 0 .../lib/Service/CustomerService.php | 0 .../lib/Service/CustomerSessionService.php | 0 .../stripe-php/lib/Service/DisputeService.php | 0 .../Entitlements/ActiveEntitlementService.php | 0 .../EntitlementsServiceFactory.php | 0 .../Service/Entitlements/FeatureService.php | 0 .../lib/Service/EphemeralKeyService.php | 0 .../stripe-php/lib/Service/EventService.php | 0 .../lib/Service/ExchangeRateService.php | 0 .../lib/Service/FileLinkService.php | 0 .../stripe-php/lib/Service/FileService.php | 0 .../FinancialConnections/AccountService.php | 0 .../FinancialConnectionsServiceFactory.php | 0 .../FinancialConnections/SessionService.php | 0 .../TransactionService.php | 0 .../Forwarding/ForwardingServiceFactory.php | 0 .../lib/Service/Forwarding/RequestService.php | 0 .../Identity/IdentityServiceFactory.php | 0 .../Identity/VerificationReportService.php | 0 .../Identity/VerificationSessionService.php | 0 .../lib/Service/InvoiceItemService.php | 0 .../stripe-php/lib/Service/InvoiceService.php | 0 .../Service/Issuing/AuthorizationService.php | 0 .../lib/Service/Issuing/CardService.php | 0 .../lib/Service/Issuing/CardholderService.php | 0 .../lib/Service/Issuing/DisputeService.php | 0 .../Service/Issuing/IssuingServiceFactory.php | 0 .../Issuing/PersonalizationDesignService.php | 0 .../Service/Issuing/PhysicalBundleService.php | 0 .../lib/Service/Issuing/TokenService.php | 0 .../Service/Issuing/TransactionService.php | 0 .../stripe-php/lib/Service/MandateService.php | 0 .../stripe-php/lib/Service/OAuthService.php | 0 .../lib/Service/PaymentIntentService.php | 0 .../lib/Service/PaymentLinkService.php | 0 .../PaymentMethodConfigurationService.php | 0 .../Service/PaymentMethodDomainService.php | 0 .../lib/Service/PaymentMethodService.php | 0 .../stripe-php/lib/Service/PayoutService.php | 0 .../stripe-php/lib/Service/PlanService.php | 0 .../stripe-php/lib/Service/PriceService.php | 0 .../stripe-php/lib/Service/ProductService.php | 0 .../lib/Service/PromotionCodeService.php | 0 .../stripe-php/lib/Service/QuoteService.php | 0 .../Radar/EarlyFraudWarningService.php | 0 .../lib/Service/Radar/RadarServiceFactory.php | 0 .../Service/Radar/ValueListItemService.php | 0 .../lib/Service/Radar/ValueListService.php | 0 .../stripe-php/lib/Service/RefundService.php | 0 .../Service/Reporting/ReportRunService.php | 0 .../Service/Reporting/ReportTypeService.php | 0 .../Reporting/ReportingServiceFactory.php | 0 .../stripe-php/lib/Service/ReviewService.php | 0 .../lib/Service/SetupAttemptService.php | 0 .../lib/Service/SetupIntentService.php | 0 .../lib/Service/ShippingRateService.php | 0 .../Sigma/ScheduledQueryRunService.php | 0 .../lib/Service/Sigma/SigmaServiceFactory.php | 0 .../stripe-php/lib/Service/SourceService.php | 0 .../lib/Service/SubscriptionItemService.php | 0 .../Service/SubscriptionScheduleService.php | 0 .../lib/Service/SubscriptionService.php | 0 .../lib/Service/Tax/CalculationService.php | 0 .../lib/Service/Tax/RegistrationService.php | 0 .../lib/Service/Tax/SettingsService.php | 0 .../lib/Service/Tax/TaxServiceFactory.php | 0 .../lib/Service/Tax/TransactionService.php | 0 .../stripe-php/lib/Service/TaxCodeService.php | 0 .../stripe-php/lib/Service/TaxIdService.php | 0 .../stripe-php/lib/Service/TaxRateService.php | 0 .../Service/Terminal/ConfigurationService.php | 0 .../Terminal/ConnectionTokenService.php | 0 .../lib/Service/Terminal/LocationService.php | 0 .../lib/Service/Terminal/ReaderService.php | 0 .../Terminal/TerminalServiceFactory.php | 0 .../TestHelpers/ConfirmationTokenService.php | 0 .../Service/TestHelpers/CustomerService.php | 0 .../Issuing/AuthorizationService.php | 0 .../TestHelpers/Issuing/CardService.php | 0 .../Issuing/IssuingServiceFactory.php | 0 .../Issuing/PersonalizationDesignService.php | 0 .../Issuing/TransactionService.php | 0 .../lib/Service/TestHelpers/RefundService.php | 0 .../TestHelpers/Terminal/ReaderService.php | 0 .../Terminal/TerminalServiceFactory.php | 0 .../Service/TestHelpers/TestClockService.php | 0 .../TestHelpers/TestHelpersServiceFactory.php | 0 .../Treasury/InboundTransferService.php | 0 .../Treasury/OutboundPaymentService.php | 0 .../Treasury/OutboundTransferService.php | 0 .../Treasury/ReceivedCreditService.php | 0 .../Treasury/ReceivedDebitService.php | 0 .../Treasury/TreasuryServiceFactory.php | 0 .../stripe-php/lib/Service/TokenService.php | 0 .../stripe-php/lib/Service/TopupService.php | 0 .../lib/Service/TransferService.php | 0 .../Treasury/CreditReversalService.php | 0 .../Service/Treasury/DebitReversalService.php | 0 .../Treasury/FinancialAccountService.php | 0 .../Treasury/InboundTransferService.php | 0 .../Treasury/OutboundPaymentService.php | 0 .../Treasury/OutboundTransferService.php | 0 .../Treasury/ReceivedCreditService.php | 0 .../Service/Treasury/ReceivedDebitService.php | 0 .../Treasury/TransactionEntryService.php | 0 .../Service/Treasury/TransactionService.php | 0 .../Treasury/TreasuryServiceFactory.php | 0 .../lib/Service/WebhookEndpointService.php | 0 vendor/stripe/stripe-php/lib/SetupAttempt.php | 0 vendor/stripe/stripe-php/lib/SetupIntent.php | 0 vendor/stripe/stripe-php/lib/ShippingRate.php | 0 .../lib/Sigma/ScheduledQueryRun.php | 0 .../stripe-php/lib/SingletonApiResource.php | 0 vendor/stripe/stripe-php/lib/Source.php | 0 .../lib/SourceMandateNotification.php | 0 .../stripe-php/lib/SourceTransaction.php | 0 vendor/stripe/stripe-php/lib/Stripe.php | 0 vendor/stripe/stripe-php/lib/StripeClient.php | 0 .../stripe-php/lib/StripeClientInterface.php | 0 vendor/stripe/stripe-php/lib/StripeObject.php | 0 .../lib/StripeStreamingClientInterface.php | 0 vendor/stripe/stripe-php/lib/Subscription.php | 0 .../stripe-php/lib/SubscriptionItem.php | 0 .../stripe-php/lib/SubscriptionSchedule.php | 0 .../stripe/stripe-php/lib/Tax/Calculation.php | 0 .../lib/Tax/CalculationLineItem.php | 0 .../stripe-php/lib/Tax/Registration.php | 0 vendor/stripe/stripe-php/lib/Tax/Settings.php | 0 .../stripe/stripe-php/lib/Tax/Transaction.php | 0 .../lib/Tax/TransactionLineItem.php | 0 vendor/stripe/stripe-php/lib/TaxCode.php | 0 .../stripe-php/lib/TaxDeductedAtSource.php | 0 vendor/stripe/stripe-php/lib/TaxId.php | 0 vendor/stripe/stripe-php/lib/TaxRate.php | 0 .../stripe-php/lib/Terminal/Configuration.php | 0 .../lib/Terminal/ConnectionToken.php | 0 .../stripe-php/lib/Terminal/Location.php | 0 .../stripe/stripe-php/lib/Terminal/Reader.php | 0 .../stripe-php/lib/TestHelpers/TestClock.php | 0 vendor/stripe/stripe-php/lib/Token.php | 0 vendor/stripe/stripe-php/lib/Topup.php | 0 vendor/stripe/stripe-php/lib/Transfer.php | 0 .../stripe-php/lib/TransferReversal.php | 0 .../lib/Treasury/CreditReversal.php | 0 .../stripe-php/lib/Treasury/DebitReversal.php | 0 .../lib/Treasury/FinancialAccount.php | 0 .../lib/Treasury/FinancialAccountFeatures.php | 0 .../lib/Treasury/InboundTransfer.php | 0 .../lib/Treasury/OutboundPayment.php | 0 .../lib/Treasury/OutboundTransfer.php | 0 .../lib/Treasury/ReceivedCredit.php | 0 .../stripe-php/lib/Treasury/ReceivedDebit.php | 0 .../stripe-php/lib/Treasury/Transaction.php | 0 .../lib/Treasury/TransactionEntry.php | 0 vendor/stripe/stripe-php/lib/UsageRecord.php | 0 .../stripe-php/lib/UsageRecordSummary.php | 0 .../stripe/stripe-php/lib/Util/ApiVersion.php | 0 .../lib/Util/CaseInsensitiveArray.php | 0 .../stripe-php/lib/Util/DefaultLogger.php | 0 .../stripe-php/lib/Util/LoggerInterface.php | 0 .../stripe-php/lib/Util/ObjectTypes.php | 0 .../stripe-php/lib/Util/RandomGenerator.php | 0 .../stripe-php/lib/Util/RequestOptions.php | 0 vendor/stripe/stripe-php/lib/Util/Set.php | 0 vendor/stripe/stripe-php/lib/Util/Util.php | 0 vendor/stripe/stripe-php/lib/Webhook.php | 0 .../stripe/stripe-php/lib/WebhookEndpoint.php | 0 .../stripe-php/lib/WebhookSignature.php | 0 vendor/symfony/.DS_Store | Bin .../deprecation-contracts/CHANGELOG 2.md | 0 .../deprecation-contracts/CHANGELOG.md | 0 vendor/symfony/deprecation-contracts/LICENSE | 0 .../symfony/deprecation-contracts/LICENSE 2 | 0 .../symfony/deprecation-contracts/README 2.md | 0 .../symfony/deprecation-contracts/README.md | 0 .../deprecation-contracts/composer 2.json | 0 .../deprecation-contracts/composer.json | 0 .../deprecation-contracts/function 2.php | 0 .../deprecation-contracts/function.php | 0 vendor/symfony/filesystem/CHANGELOG.md | 0 .../Exception/ExceptionInterface.php | 0 .../Exception/FileNotFoundException.php | 0 .../filesystem/Exception/IOException.php | 0 .../Exception/IOExceptionInterface.php | 0 .../Exception/InvalidArgumentException.php | 0 .../filesystem/Exception/RuntimeException.php | 0 vendor/symfony/filesystem/Filesystem.php | 0 vendor/symfony/filesystem/LICENSE | 0 vendor/symfony/filesystem/Path.php | 0 vendor/symfony/filesystem/README.md | 0 vendor/symfony/filesystem/composer.json | 0 vendor/symfony/polyfill-ctype/Ctype 2.php | 0 vendor/symfony/polyfill-ctype/Ctype.php | 0 vendor/symfony/polyfill-ctype/LICENSE | 0 vendor/symfony/polyfill-ctype/LICENSE 2 | 0 vendor/symfony/polyfill-ctype/README 2.md | 0 vendor/symfony/polyfill-ctype/README.md | 0 vendor/symfony/polyfill-ctype/bootstrap 2.php | 0 vendor/symfony/polyfill-ctype/bootstrap.php | 0 .../symfony/polyfill-ctype/bootstrap80 2.php | 0 vendor/symfony/polyfill-ctype/bootstrap80.php | 0 vendor/symfony/polyfill-ctype/composer 2.json | 0 vendor/symfony/polyfill-ctype/composer.json | 0 vendor/symfony/polyfill-mbstring/LICENSE | 0 vendor/symfony/polyfill-mbstring/LICENSE 2 | 0 .../symfony/polyfill-mbstring/Mbstring 2.php | 0 vendor/symfony/polyfill-mbstring/Mbstring.php | 0 vendor/symfony/polyfill-mbstring/README 2.md | 0 vendor/symfony/polyfill-mbstring/README.md | 0 .../Resources/unidata/caseFolding.php | 0 .../Resources/unidata/lowerCase.php | 0 .../Resources/unidata/titleCaseRegexp.php | 0 .../Resources/unidata/upperCase.php | 0 .../symfony/polyfill-mbstring/bootstrap 2.php | 0 .../symfony/polyfill-mbstring/bootstrap.php | 0 .../polyfill-mbstring/bootstrap80 2.php | 0 .../symfony/polyfill-mbstring/bootstrap80.php | 0 .../symfony/polyfill-mbstring/composer 2.json | 0 .../symfony/polyfill-mbstring/composer.json | 0 .../.github/workflows/main.yml | 0 vendor/ubnt/ucrm-plugin-sdk/.gitignore | 0 vendor/ubnt/ucrm-plugin-sdk/LICENSE.md | 0 vendor/ubnt/ucrm-plugin-sdk/Makefile | 0 vendor/ubnt/ucrm-plugin-sdk/README.md | 0 vendor/ubnt/ucrm-plugin-sdk/bin/pack-plugin | 0 vendor/ubnt/ucrm-plugin-sdk/composer.json | 0 vendor/ubnt/ucrm-plugin-sdk/ecs.php | 0 vendor/ubnt/ucrm-plugin-sdk/phpstan.neon | 0 vendor/ubnt/ucrm-plugin-sdk/phpunit.xml | 0 .../src/UcrmPluginSdk/Data/UcrmOptions.php | 0 .../src/UcrmPluginSdk/Data/UcrmUser.php | 0 .../Exception/ConfigurationException.php | 0 .../InvalidPluginRootPathException.php | 0 .../UcrmPluginSdk/Exception/JsonException.php | 0 .../Exception/UcrmPluginSdkException.php | 0 .../src/UcrmPluginSdk/Security/Permission.php | 0 .../Security/PermissionNames.php | 0 .../Security/SpecialPermission.php | 0 .../Security/SpecialPermissionNames.php | 0 .../Service/AbstractOptionsManager.php | 0 .../Service/PluginConfigManager.php | 0 .../Service/PluginLogManager.php | 0 .../src/UcrmPluginSdk/Service/UcrmApi.php | 0 .../Service/UcrmOptionsManager.php | 0 .../UcrmPluginSdk/Service/UcrmSecurity.php | 0 .../src/UcrmPluginSdk/Service/UnmsApi.php | 0 .../src/UcrmPluginSdk/Util/Helpers.php | 0 .../src/UcrmPluginSdk/Util/Json.php | 0 .../tests/UcrmPluginSdk/Data/UcrmUserTest.php | 0 .../Service/PluginConfigManagerTest.php | 0 .../Service/PluginLogManagerTest.php | 0 .../UcrmPluginSdk/Service/UcrmApiTest.php | 0 .../Service/UcrmOptionsManagerTest.php | 0 .../Service/UcrmSecurityTest.php | 0 .../UcrmPluginSdk/Service/UnmsApiTest.php | 0 .../tests/UcrmPluginSdk/Util/HelpersTest.php | 0 .../tests/UcrmPluginSdk/Util/JsonTest.php | 0 .../tests/files_disabled/ucrm.json | 0 .../tests/files_enabled/data/config.json | 0 .../tests/files_enabled/ucrm.json | 0 628 files changed, 7449 insertions(+), 29 deletions(-) mode change 100644 => 100755 .DS_Store mode change 100644 => 100755 .gitignore mode change 100644 => 100755 CHANGELOG.md create mode 100755 PaymentIntentStripe.php mode change 100644 => 100755 README.md create mode 100755 archivos_webhook_destino/main.php create mode 100755 archivos_webhook_destino/public.php create mode 100755 archivos_webhook_destino/src/Facade/AbstractMessageNotifierFacade.php create mode 100755 archivos_webhook_destino/src/Facade/AbstractOxxoOperationsFacade.php create mode 100755 archivos_webhook_destino/src/Facade/AbstractStripeOperationsFacade.php create mode 100755 archivos_webhook_destino/src/Facade/AbstractUpdateClientFacade.php create mode 100755 archivos_webhook_destino/src/Facade/ClientCallBellAPI.php create mode 100755 archivos_webhook_destino/src/Facade/PluginNotifierFacade.php create mode 100755 archivos_webhook_destino/src/Facade/PluginOxxoNotifierFacade.php create mode 100755 archivos_webhook_destino/src/Facade/TwilioNotifierFacade.php create mode 100755 archivos_webhook_destino/src/Facade/pruebas_ucrm_api.php create mode 100755 archivos_webhook_destino/src/Plugin.php mode change 100644 => 100755 composer.json mode change 100644 => 100755 composer.lock mode change 100644 => 100755 data/config.json mode change 100644 => 100755 data/plugin.log mode change 100644 => 100755 img/vista-general.png mode change 100644 => 100755 main.php mode change 100644 => 100755 manifest.json mode change 100644 => 100755 public.php mode change 100644 => 100755 src/PaymentIntentService.php mode change 100644 => 100755 ucrm.json mode change 100644 => 100755 vendor/.DS_Store mode change 100644 => 100755 vendor/autoload.php mode change 100644 => 100755 vendor/bin/pack-plugin mode change 100644 => 100755 vendor/composer/ClassLoader.php mode change 100644 => 100755 vendor/composer/InstalledVersions.php mode change 100644 => 100755 vendor/composer/LICENSE mode change 100644 => 100755 vendor/composer/autoload_classmap.php mode change 100644 => 100755 vendor/composer/autoload_files.php mode change 100644 => 100755 vendor/composer/autoload_namespaces.php mode change 100644 => 100755 vendor/composer/autoload_psr4.php mode change 100644 => 100755 vendor/composer/autoload_real.php mode change 100644 => 100755 vendor/composer/autoload_static.php mode change 100644 => 100755 vendor/composer/installed.json mode change 100644 => 100755 vendor/composer/installed.php mode change 100644 => 100755 vendor/composer/platform_check.php mode change 100644 => 100755 vendor/guzzlehttp/.DS_Store mode change 100644 => 100755 vendor/guzzlehttp/guzzle/CHANGELOG 2.md mode change 100644 => 100755 vendor/guzzlehttp/guzzle/CHANGELOG.md mode change 100644 => 100755 vendor/guzzlehttp/guzzle/LICENSE mode change 100644 => 100755 vendor/guzzlehttp/guzzle/LICENSE 2 mode change 100644 => 100755 vendor/guzzlehttp/guzzle/README 2.md mode change 100644 => 100755 vendor/guzzlehttp/guzzle/README.md mode change 100644 => 100755 vendor/guzzlehttp/guzzle/UPGRADING 2.md mode change 100644 => 100755 vendor/guzzlehttp/guzzle/UPGRADING.md mode change 100644 => 100755 vendor/guzzlehttp/guzzle/composer 2.json mode change 100644 => 100755 vendor/guzzlehttp/guzzle/composer.json mode change 100644 => 100755 vendor/guzzlehttp/guzzle/package-lock.json mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/BodySummarizer.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Client.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/ClientInterface.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/ClientTrait.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/ClientException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/InvalidArgumentException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/RequestException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/ServerException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Exception/TransferException.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/Proxy.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/HandlerStack.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/MessageFormatter.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Middleware.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Pool.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/RequestOptions.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/RetryMiddleware.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/TransferStats.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/Utils.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/functions.php mode change 100644 => 100755 vendor/guzzlehttp/guzzle/src/functions_include.php mode change 100644 => 100755 vendor/guzzlehttp/promises/CHANGELOG 2.md mode change 100644 => 100755 vendor/guzzlehttp/promises/CHANGELOG.md mode change 100644 => 100755 vendor/guzzlehttp/promises/LICENSE mode change 100644 => 100755 vendor/guzzlehttp/promises/LICENSE 2 mode change 100644 => 100755 vendor/guzzlehttp/promises/README 2.md mode change 100644 => 100755 vendor/guzzlehttp/promises/README.md mode change 100644 => 100755 vendor/guzzlehttp/promises/composer 2.json mode change 100644 => 100755 vendor/guzzlehttp/promises/composer.json mode change 100644 => 100755 vendor/guzzlehttp/promises/src/AggregateException.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/CancellationException.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/Coroutine.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/Create.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/Each.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/EachPromise.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/FulfilledPromise.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/Is.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/Promise.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/PromiseInterface.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/PromisorInterface.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/RejectedPromise.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/RejectionException.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/TaskQueue.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/TaskQueueInterface.php mode change 100644 => 100755 vendor/guzzlehttp/promises/src/Utils.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/CHANGELOG.md mode change 100644 => 100755 vendor/guzzlehttp/psr7/LICENSE mode change 100644 => 100755 vendor/guzzlehttp/psr7/README.md mode change 100644 => 100755 vendor/guzzlehttp/psr7/composer.json mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/AppendStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/BufferStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/CachingStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/DroppingStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/FnStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Header.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/HttpFactory.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/InflateStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/LazyOpenStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/LimitStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Message.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/MessageTrait.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/MimeType.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/MultipartStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/NoSeekStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/PumpStream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Query.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Request.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Response.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Rfc7230.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/ServerRequest.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Stream.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/StreamWrapper.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/UploadedFile.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Uri.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/UriComparator.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/UriNormalizer.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/UriResolver.php mode change 100644 => 100755 vendor/guzzlehttp/psr7/src/Utils.php mode change 100644 => 100755 vendor/psr/.DS_Store mode change 100644 => 100755 vendor/psr/http-client/CHANGELOG.md mode change 100644 => 100755 vendor/psr/http-client/LICENSE mode change 100644 => 100755 vendor/psr/http-client/README.md mode change 100644 => 100755 vendor/psr/http-client/composer.json mode change 100644 => 100755 vendor/psr/http-client/src/ClientExceptionInterface.php mode change 100644 => 100755 vendor/psr/http-client/src/ClientInterface.php mode change 100644 => 100755 vendor/psr/http-client/src/NetworkExceptionInterface.php mode change 100644 => 100755 vendor/psr/http-client/src/RequestExceptionInterface.php mode change 100644 => 100755 vendor/psr/http-factory/LICENSE mode change 100644 => 100755 vendor/psr/http-factory/LICENSE 2 mode change 100644 => 100755 vendor/psr/http-factory/README 2.md mode change 100644 => 100755 vendor/psr/http-factory/README.md mode change 100644 => 100755 vendor/psr/http-factory/composer 2.json mode change 100644 => 100755 vendor/psr/http-factory/composer.json mode change 100644 => 100755 vendor/psr/http-factory/src/RequestFactoryInterface.php mode change 100644 => 100755 vendor/psr/http-factory/src/ResponseFactoryInterface.php mode change 100644 => 100755 vendor/psr/http-factory/src/ServerRequestFactoryInterface.php mode change 100644 => 100755 vendor/psr/http-factory/src/StreamFactoryInterface.php mode change 100644 => 100755 vendor/psr/http-factory/src/UploadedFileFactoryInterface.php mode change 100644 => 100755 vendor/psr/http-factory/src/UriFactoryInterface.php mode change 100644 => 100755 vendor/psr/http-message/CHANGELOG.md mode change 100644 => 100755 vendor/psr/http-message/LICENSE mode change 100644 => 100755 vendor/psr/http-message/README.md mode change 100644 => 100755 vendor/psr/http-message/composer.json mode change 100644 => 100755 vendor/psr/http-message/docs/PSR7-Interfaces.md mode change 100644 => 100755 vendor/psr/http-message/docs/PSR7-Usage.md mode change 100644 => 100755 vendor/psr/http-message/src/MessageInterface.php mode change 100644 => 100755 vendor/psr/http-message/src/RequestInterface.php mode change 100644 => 100755 vendor/psr/http-message/src/ResponseInterface.php mode change 100644 => 100755 vendor/psr/http-message/src/ServerRequestInterface.php mode change 100644 => 100755 vendor/psr/http-message/src/StreamInterface.php mode change 100644 => 100755 vendor/psr/http-message/src/UploadedFileInterface.php mode change 100644 => 100755 vendor/psr/http-message/src/UriInterface.php mode change 100644 => 100755 vendor/ralouphie/getallheaders/LICENSE mode change 100644 => 100755 vendor/ralouphie/getallheaders/README.md mode change 100644 => 100755 vendor/ralouphie/getallheaders/composer.json mode change 100644 => 100755 vendor/ralouphie/getallheaders/src/getallheaders.php mode change 100644 => 100755 vendor/stripe/stripe-php/.gitignore mode change 100644 => 100755 vendor/stripe/stripe-php/CHANGELOG.md mode change 100644 => 100755 vendor/stripe/stripe-php/LICENSE mode change 100644 => 100755 vendor/stripe/stripe-php/OPENAPI_VERSION mode change 100644 => 100755 vendor/stripe/stripe-php/README.md mode change 100644 => 100755 vendor/stripe/stripe-php/VERSION mode change 100644 => 100755 vendor/stripe/stripe-php/composer.json mode change 100644 => 100755 vendor/stripe/stripe-php/data/ca-certificates.crt mode change 100644 => 100755 vendor/stripe/stripe-php/init.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Account.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/AccountLink.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/AccountSession.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/All.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/Create.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/Delete.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/NestedResource.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/Request.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/Retrieve.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/Search.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/SingletonRetrieve.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiOperations/Update.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiRequestor.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiResource.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApiResponse.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApplePayDomain.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Application.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApplicationFee.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ApplicationFeeRefund.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Apps/Secret.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Balance.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/BalanceTransaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/BankAccount.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/BaseStripeClient.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/BaseStripeClientInterface.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Billing/Meter.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Billing/MeterEvent.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Billing/MeterEventAdjustment.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Billing/MeterEventSummary.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/BillingPortal/Configuration.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/BillingPortal/Session.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Capability.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Card.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/CashBalance.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Charge.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Checkout/Session.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Climate/Order.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Climate/Product.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Climate/Supplier.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Collection.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ConfirmationToken.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ConnectCollectionTransfer.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/CountrySpec.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Coupon.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/CreditNote.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/CreditNoteLineItem.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Customer.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/CustomerBalanceTransaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/CustomerCashBalanceTransaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/CustomerSession.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Discount.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Dispute.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Entitlements/ActiveEntitlement.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Entitlements/Feature.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/EphemeralKey.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ErrorObject.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Event.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/ApiConnectionException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/ApiErrorException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/AuthenticationException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/BadMethodCallException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/CardException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/ExceptionInterface.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/IdempotencyException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/InvalidArgumentException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/InvalidRequestException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/ExceptionInterface.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/InvalidClientException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/InvalidGrantException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/InvalidRequestException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/InvalidScopeException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/OAuthErrorException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/UnknownOAuthErrorException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/UnsupportedGrantTypeException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/OAuth/UnsupportedResponseTypeException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/PermissionException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/RateLimitException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/SignatureVerificationException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/UnexpectedValueException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Exception/UnknownApiErrorException.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ExchangeRate.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/File.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/FileLink.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/FinancialConnections/Account.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/FinancialConnections/AccountOwner.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/FinancialConnections/AccountOwnership.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/FinancialConnections/Session.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/FinancialConnections/Transaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Forwarding/Request.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/FundingInstructions.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/HttpClient/ClientInterface.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/HttpClient/CurlClient.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/HttpClient/StreamingClientInterface.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Identity/VerificationReport.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Identity/VerificationSession.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Invoice.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/InvoiceItem.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/InvoiceLineItem.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/Authorization.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/Card.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/CardDetails.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/Cardholder.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/Dispute.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/PersonalizationDesign.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/PhysicalBundle.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/Token.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Issuing/Transaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/LineItem.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/LoginLink.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Mandate.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/OAuth.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/OAuthErrorObject.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/PaymentIntent.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/PaymentLink.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/PaymentMethod.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/PaymentMethodConfiguration.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/PaymentMethodDomain.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Payout.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Person.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Plan.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/PlatformTaxFee.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Price.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Product.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ProductFeature.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/PromotionCode.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Quote.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Radar/EarlyFraudWarning.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Radar/ValueList.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Radar/ValueListItem.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/RecipientTransfer.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Refund.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Reporting/ReportRun.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Reporting/ReportType.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/RequestTelemetry.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ReserveTransaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Review.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/SearchResult.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/AbstractService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/AbstractServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/AccountLinkService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/AccountService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/AccountSessionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/ApplePayDomainService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/ApplicationFeeService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Apps/AppsServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Apps/SecretService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/BalanceService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/BalanceTransactionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Billing/BillingServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Billing/MeterEventAdjustmentService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Billing/MeterEventService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Billing/MeterService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/BillingPortal/BillingPortalServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/BillingPortal/ConfigurationService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/BillingPortal/SessionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/ChargeService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Checkout/CheckoutServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Checkout/SessionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Climate/ClimateServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Climate/OrderService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Climate/ProductService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Climate/SupplierService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/ConfirmationTokenService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/CoreServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/CountrySpecService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/CouponService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/CreditNoteService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/CustomerService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/CustomerSessionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/DisputeService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Entitlements/ActiveEntitlementService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Entitlements/EntitlementsServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Entitlements/FeatureService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/EphemeralKeyService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/EventService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/ExchangeRateService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/FileLinkService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/FileService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/FinancialConnections/AccountService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/FinancialConnections/FinancialConnectionsServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/FinancialConnections/SessionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/FinancialConnections/TransactionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Forwarding/ForwardingServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Forwarding/RequestService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Identity/IdentityServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Identity/VerificationReportService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Identity/VerificationSessionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/InvoiceItemService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/InvoiceService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/AuthorizationService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/CardService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/CardholderService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/DisputeService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/IssuingServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/PersonalizationDesignService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/PhysicalBundleService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/TokenService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Issuing/TransactionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/MandateService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/OAuthService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PaymentIntentService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PaymentLinkService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PaymentMethodConfigurationService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PaymentMethodDomainService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PaymentMethodService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PayoutService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PlanService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PriceService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/ProductService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/PromotionCodeService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/QuoteService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Radar/EarlyFraudWarningService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Radar/RadarServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Radar/ValueListItemService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Radar/ValueListService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/RefundService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Reporting/ReportRunService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Reporting/ReportTypeService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Reporting/ReportingServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/ReviewService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/SetupAttemptService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/SetupIntentService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/ShippingRateService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Sigma/ScheduledQueryRunService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Sigma/SigmaServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/SourceService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/SubscriptionItemService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/SubscriptionScheduleService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/SubscriptionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Tax/CalculationService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Tax/RegistrationService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Tax/SettingsService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Tax/TaxServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Tax/TransactionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TaxCodeService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TaxIdService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TaxRateService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Terminal/ConfigurationService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Terminal/ConnectionTokenService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Terminal/LocationService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Terminal/ReaderService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Terminal/TerminalServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/ConfirmationTokenService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/CustomerService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Issuing/AuthorizationService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Issuing/CardService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Issuing/IssuingServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Issuing/PersonalizationDesignService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Issuing/TransactionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/RefundService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Terminal/ReaderService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Terminal/TerminalServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/TestClockService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/TestHelpersServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Treasury/InboundTransferService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Treasury/OutboundPaymentService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Treasury/OutboundTransferService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Treasury/ReceivedCreditService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Treasury/ReceivedDebitService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TestHelpers/Treasury/TreasuryServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TokenService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TopupService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/TransferService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/CreditReversalService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/DebitReversalService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/FinancialAccountService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/InboundTransferService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/OutboundPaymentService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/OutboundTransferService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/ReceivedCreditService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/ReceivedDebitService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/TransactionEntryService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/TransactionService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/Treasury/TreasuryServiceFactory.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Service/WebhookEndpointService.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/SetupAttempt.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/SetupIntent.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/ShippingRate.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Sigma/ScheduledQueryRun.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/SingletonApiResource.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Source.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/SourceMandateNotification.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/SourceTransaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Stripe.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/StripeClient.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/StripeClientInterface.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/StripeObject.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/StripeStreamingClientInterface.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Subscription.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/SubscriptionItem.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/SubscriptionSchedule.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Tax/Calculation.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Tax/CalculationLineItem.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Tax/Registration.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Tax/Settings.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Tax/Transaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Tax/TransactionLineItem.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/TaxCode.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/TaxDeductedAtSource.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/TaxId.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/TaxRate.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Terminal/Configuration.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Terminal/ConnectionToken.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Terminal/Location.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Terminal/Reader.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/TestHelpers/TestClock.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Token.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Topup.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Transfer.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/TransferReversal.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/CreditReversal.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/DebitReversal.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/FinancialAccount.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/FinancialAccountFeatures.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/InboundTransfer.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/OutboundPayment.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/OutboundTransfer.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/ReceivedCredit.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/ReceivedDebit.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/Transaction.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Treasury/TransactionEntry.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/UsageRecord.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/UsageRecordSummary.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/ApiVersion.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/CaseInsensitiveArray.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/DefaultLogger.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/LoggerInterface.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/ObjectTypes.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/RandomGenerator.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/RequestOptions.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/Set.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Util/Util.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/Webhook.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/WebhookEndpoint.php mode change 100644 => 100755 vendor/stripe/stripe-php/lib/WebhookSignature.php mode change 100644 => 100755 vendor/symfony/.DS_Store mode change 100644 => 100755 vendor/symfony/deprecation-contracts/CHANGELOG 2.md mode change 100644 => 100755 vendor/symfony/deprecation-contracts/CHANGELOG.md mode change 100644 => 100755 vendor/symfony/deprecation-contracts/LICENSE mode change 100644 => 100755 vendor/symfony/deprecation-contracts/LICENSE 2 mode change 100644 => 100755 vendor/symfony/deprecation-contracts/README 2.md mode change 100644 => 100755 vendor/symfony/deprecation-contracts/README.md mode change 100644 => 100755 vendor/symfony/deprecation-contracts/composer 2.json mode change 100644 => 100755 vendor/symfony/deprecation-contracts/composer.json mode change 100644 => 100755 vendor/symfony/deprecation-contracts/function 2.php mode change 100644 => 100755 vendor/symfony/deprecation-contracts/function.php mode change 100644 => 100755 vendor/symfony/filesystem/CHANGELOG.md mode change 100644 => 100755 vendor/symfony/filesystem/Exception/ExceptionInterface.php mode change 100644 => 100755 vendor/symfony/filesystem/Exception/FileNotFoundException.php mode change 100644 => 100755 vendor/symfony/filesystem/Exception/IOException.php mode change 100644 => 100755 vendor/symfony/filesystem/Exception/IOExceptionInterface.php mode change 100644 => 100755 vendor/symfony/filesystem/Exception/InvalidArgumentException.php mode change 100644 => 100755 vendor/symfony/filesystem/Exception/RuntimeException.php mode change 100644 => 100755 vendor/symfony/filesystem/Filesystem.php mode change 100644 => 100755 vendor/symfony/filesystem/LICENSE mode change 100644 => 100755 vendor/symfony/filesystem/Path.php mode change 100644 => 100755 vendor/symfony/filesystem/README.md mode change 100644 => 100755 vendor/symfony/filesystem/composer.json mode change 100644 => 100755 vendor/symfony/polyfill-ctype/Ctype 2.php mode change 100644 => 100755 vendor/symfony/polyfill-ctype/Ctype.php mode change 100644 => 100755 vendor/symfony/polyfill-ctype/LICENSE mode change 100644 => 100755 vendor/symfony/polyfill-ctype/LICENSE 2 mode change 100644 => 100755 vendor/symfony/polyfill-ctype/README 2.md mode change 100644 => 100755 vendor/symfony/polyfill-ctype/README.md mode change 100644 => 100755 vendor/symfony/polyfill-ctype/bootstrap 2.php mode change 100644 => 100755 vendor/symfony/polyfill-ctype/bootstrap.php mode change 100644 => 100755 vendor/symfony/polyfill-ctype/bootstrap80 2.php mode change 100644 => 100755 vendor/symfony/polyfill-ctype/bootstrap80.php mode change 100644 => 100755 vendor/symfony/polyfill-ctype/composer 2.json mode change 100644 => 100755 vendor/symfony/polyfill-ctype/composer.json mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/LICENSE mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/LICENSE 2 mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/Mbstring 2.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/Mbstring.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/README 2.md mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/README.md mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/bootstrap 2.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/bootstrap.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/bootstrap80 2.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/bootstrap80.php mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/composer 2.json mode change 100644 => 100755 vendor/symfony/polyfill-mbstring/composer.json mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/.github/workflows/main.yml mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/.gitignore mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/LICENSE.md mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/Makefile mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/README.md mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/bin/pack-plugin mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/composer.json mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/ecs.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/phpstan.neon mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/phpunit.xml mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Data/UcrmOptions.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Data/UcrmUser.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Exception/ConfigurationException.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Exception/InvalidPluginRootPathException.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Exception/JsonException.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Exception/UcrmPluginSdkException.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Security/Permission.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Security/PermissionNames.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Security/SpecialPermission.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Security/SpecialPermissionNames.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Service/AbstractOptionsManager.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Service/PluginConfigManager.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Service/PluginLogManager.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Service/UcrmApi.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Service/UcrmOptionsManager.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Service/UcrmSecurity.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Service/UnmsApi.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Util/Helpers.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/src/UcrmPluginSdk/Util/Json.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Data/UcrmUserTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Service/PluginConfigManagerTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Service/PluginLogManagerTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Service/UcrmApiTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Service/UcrmOptionsManagerTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Service/UcrmSecurityTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Service/UnmsApiTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Util/HelpersTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/UcrmPluginSdk/Util/JsonTest.php mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/files_disabled/ucrm.json mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/files_enabled/data/config.json mode change 100644 => 100755 vendor/ubnt/ucrm-plugin-sdk/tests/files_enabled/ucrm.json diff --git a/.DS_Store b/.DS_Store old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 83784c4..4286f7a --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.zip -unmscrm.apib \ No newline at end of file +unmscrm.apib +/ucrm.json +/data/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100644 new mode 100755 index 7c43a1d..1ad5ebe --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.2.0] - 23-12-2025 +### Added +- Resolución dinámica del ID del método de pago ("Transferencia bancaria") para mayor portabilidad entre sistemas UISP. +- Manejo del evento de webhook `customer_cash_balance_transaction.created` para registrar pagos automáticos cuando se fondea una cuenta Stripe. +### Fixed +- Error de validación de API 422: Cast explícito de `clientId` a integer. +- Error de validación de API 422: Cambio de parámetro `method` a `methodId` y cast a string (GUID). + ## [1.1.0] - 16-12-2025 ### Added - Soporte para Modo Oscuro (Dark Mode) con detección automática y toggle manual. diff --git a/PaymentIntentStripe.php b/PaymentIntentStripe.php new file mode 100755 index 0000000..bfa54a5 --- /dev/null +++ b/PaymentIntentStripe.php @@ -0,0 +1,635 @@ +setTimezone(new DateTimeZone('UTC'))->getTimestamp(); +$endDate = (new DateTime('@' . $endDateLocal))->setTimezone(new DateTimeZone('UTC'))->getTimestamp(); + +$amount = 200; //Cantidad a cobrar +//$amount = intval($amount * 100); //Conversión de la cantidad a centavos, solo referencia, no usar +$customer_id_stripe = 'cus_PdXOlNaXGOxvlw'; //ID del cliente en Stripe +$clientId_CRM = 409; //ID del cliente en UISP CRM +//agregar constante de id de administrador 5472 +$signedAdminId = 5472; //ID del administrador que realiza la operación +$produccion = true; //Variable para indicar si se está en modo producción o no + +$clientGuzzleHttp = new Client([ + 'base_uri' => $baseUri, + 'headers' => [ + 'X-Auth-App-Key' => $token, + 'Accept' => 'application/json', + ], + 'verify' => false, +]); + +if ($produccion) { + $stripe = new \Stripe\StripeClient('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'); //Token de clave privada en modo prodcucción para la API de Stripe + $baseUri = 'https://sistema.siip.mx/crm/api/v1.0/'; //endpoint de la API REST del CRM + // $token = '7adc9198-50b1-41d0-9bfa-d4946902ed89'; //Token para acceder a los recursos del CRM 2025 + // $token = '079c28f5-888c-457d-bd7a-0a4202590f75'; //Token para acceder a los recursos del CRM + $token = '72VoFACForJQzveorR1sTLrXXwrnnK/oy6Bp9luwFTGC/dRdeQWNmFZqJeHuUzqK'; //Token para acceder a los recursos del CRM actualizado +} else { + $stripe = new \Stripe\StripeClient('sk_test_51OkG0REFY1WEUtgRH6UxBK5pu80Aq5Iy8EcdPnf0cOWzuVLQTpyLCd7CbPzqMsWMafZOHElCxhEHF7g8boURjWlJ00tBwE0W1M'); //Token de clave privada en modo prueba para la API de Stripe + $baseUri = 'https://venus.siip.mx/crm/api/v1.0/'; //endpoint de la API REST del CRM + // $token = '6abef18c-783d-4dd0-b530-be6e6a7bbd1d'; //Token para acceder a los recursos del CRM + $token = 'gvcnIJqXdUjneVSjhl6THLlQcYXJyIFCcwHKVba2bvIrNraanCTb5VeoWuJ0TFZ9'; //Token para acceder a los recursos del CRM actualizado + +} + + +createPaymentIntent($stripe, $amount, $customer_id_stripe, $clientId_CRM, $signedAdminId); //Crea el PaymentIntent + + +//createOxxoPaymentIntent($stripe, $amount, $customer_id_stripe, $clientId_CRM); //Crea el PaymentIntent para OXXO +//cancelarPaymentIntent($stripe, $customer_id_stripe, $clientId_CRM); //Cancela el PaymentIntent +//cancelarOxxoPaymentIntent($stripe, $customer_id_stripe, $clientId_CRM); //Cancela el PaymentIntent para OXXO + + +//cliente GuzzleHttp para consumir API del UISP CRM + + +//descomentar para generar el archivo csv con los ID de los clientes a los que se les va a cancelar sus payment intents +// $archivoCsv = './PaymentIntentStripe.csv'; //ruta al archivo csv con los ID de los clientes a los que se les va a cancelar sus payment intents + +// $columnaDeseada = 0; // Columna para leer del archivo csv +// $esPrimeraFila = false; // Variable de control para detectar la primera fila e ignorarla ya que son las columnas +// $ucrm_client_id; + +// if (($gestor = fopen($archivoCsv, 'r')) !== false) { //Abre el archivo csv +// while (($datos = fgetcsv($gestor, 1000, ',')) !== false) { //Lee el archivo csv +// if ($esPrimeraFila) { //Ignora la primera fila +// $esPrimeraFila = false; +// continue; +// } +// $num = count($datos); //Cuenta el número de columnas +// for ($c = 0; $c < $num; $c++) { //Recorre las columnas +// sleep(2); //Espera 1 segundo +// $ucrm_client_id = $datos[$columnaDeseada]; //Obtiene el ID del cliente en UISP CRM + +// //Hace la petición GET al CRM para obtener el ID del cliente en Stripe +// $response = $clientGuzzleHttp->request('GET', 'clients/' . $ucrm_client_id); +// $responseBody = json_decode($response->getBody(), true); //Decodifica el JSON de la respuesta + +// //ejemplo de respuesta en formato JSON de la API del CRM: {"id":2,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"31 Chiapas","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"Chiapas 31, Centro, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":"Cliente espacial, el m\u00e1s chido","sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2024-01-25T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":true,"firstName":"Daniel Humberto","lastName":"Soto Villegas","username":"danydhsv","contacts":[{"id":2,"clientId":2,"email":"dhsv.141089@gmail.com","phone":"5214181878106","name":"Personal","isBilling":false,"isContact":false,"types":[{"id":1000,"name":"WhatsApp"}]},{"id":170,"clientId":2,"email":"chavoloco@homail.com","phone":"4181148783","name":"Tio","isBilling":false,"isContact":false,"types":[{"id":1003,"name":"WhatsNotifica"}]}],"attributes":[{"id":112,"clientId":2,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_PetN1dhr4rx0kX","clientZoneVisible":true},{"id":113,"clientId":2,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"0021804341999569810","clientZoneVisible":true}],"accountBalance":700.99,"accountCredit":700.99,"accountOutstanding":0.0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[{"id":2,"name":"NS EXENTO","colorBackground":"#42a3df","colorText":"#fff"}],"invitationEmailSentDate":null,"avatarColor":"#f1df43","addressGpsLat":21.1564209,"addressGpsLon":-100.9384185,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":true,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false} + +// //consutar los adeudos del cliente (accountOutstanding) y ponerlo en una variable +// $accountOutstanding = $responseBody['accountOutstanding']; + +// //Consultar el nombre completo del cliente y ponerlo en una variable +// $nombreCompleto = $responseBody['firstName'] . ' ' . $responseBody['lastName']; +// print_r("Nombre del cliente: " . $nombreCompleto); //Imprime el nombre del cliente +// logMessage("Nombre del cliente: " . $nombreCompleto); //Imprime el nombre del cliente + +// //Consultar los atributos del cliente en UISP CRM y obtener el ID del cliente en Stripe +// $attributes = $responseBody['attributes']; //Obtiene los atributos del cliente +// $stripeCustomerId = null; //Inicializa la variable para el ID del cliente en Stripe +// foreach ($attributes as $attribute) { //Recorre los atributos del cliente +// if ($attribute['key'] == 'stripeCustomerId') { //Busca el atributo con la clave 'stripeCustomerId' +// $stripeCustomerId = $attribute['value']; //Obtiene el valor del atributo 'stripeCustomerId' +// break; //Sale del ciclo +// } +// } + +// if ($stripeCustomerId == null) { //Si no se encontró el ID del cliente en Stripe +// print_r("No se encontró el ID del cliente en Stripe" . PHP_EOL); +// logMessage("No se encontró el ID del cliente en Stripe" . PHP_EOL); +// continue; //Continúa con el siguiente cliente +// } else { +// print_r("ID del cliente en Stripe: " . $stripeCustomerId); //Imprime el ID del cliente en Stripe +// logMessage("ID del cliente en Stripe: " . $stripeCustomerId); //Imprime el ID del cliente en Stripe +// if (cancelRequiresActionPaymentIntents($stripe, $stripeCustomerId )) { +// echo "Se encontraron y cancelaron PaymentIntents en estado 'requires_action'.\n"; +// logMessage("Se encontraron y cancelaron PaymentIntents en estado 'requires_action'.\n"); +// } else { +// echo "No se encontraron PaymentIntents en estado 'requires_action' para este cliente.\n"; +// logMessage("No se encontraron PaymentIntents en estado 'requires_action' para este cliente.\n"); +// } +// $saldoFinal = getSaldoFinal($stripe, $stripeCustomerId, $startDate, $endDate); //Obtiene el saldo final del cliente +// print_r("El saldo final es: " . $saldoFinal); //Imprime el saldo final del cliente +// logMessage("El saldo final es: " . $saldoFinal); //Imprime el saldo final del cliente +// if ($saldoFinal > 0) { //Si el saldo final es mayor a 0 y el adeudo es mayor a 0 o si el saldo final es mayor a 0 y el adeudo es igual a 0 +// print_r("El saldo de Stripe es $$saldoFinal y los adeudos están en $$accountOutstanding, se procede a realizar un payment Intent" . PHP_EOL); //Imprime el saldo final del cliente +// logMessage("El saldo de Stripe es $$saldoFinal y los adeudos están en $$accountOutstanding, se procede a realizar un payment Intent" .PHP_EOL); //Imprime el saldo final del cliente +// createPaymentIntent($stripe, $saldoFinal, $stripeCustomerId, $ucrm_client_id); //Crea el PaymentIntent +// } else { +// print_r("El saldo de Stripe es $$saldoFinal y los adeudos están en $$accountOutstanding, no se realizará el cobro" . PHP_EOL); //Imprime el saldo final del cliente +// logMessage("El saldo de Stripe es $$saldoFinal y los adeudos están en $$accountOutstanding, no se realizará el cobro".PHP_EOL); //Imprime el saldo final del cliente +// } + +// } + + +// } +// } +// fclose($gestor); +// } else { +// print_r("Error al abrir el archivo CSV" . PHP_EOL); +// logMessage("Error al abrir el archivo CSV" . PHP_EOL, 'error'); +// } + + +// function getSaldoFinal($stripe, $customerId, $startDate, $endDate) +// { +// $limit = 100; // Máximo de eventos por solicitud (Stripe permite un máximo de 100) +// $lastEventId = null; // Almacena el ID del último evento para la paginación +// $filteredEvents = []; // Arreglo para almacenar los eventos filtrados +// $saldoFinal = 0; // Inicializar el saldo final + +// do { +// // Obtener los eventos de transacciones de saldo en efectivo, filtrando por fechas +// $params = [ +// 'type' => 'customer_cash_balance_transaction.created', +// 'created' => [ +// 'gte' => $startDate, // Mayor o igual a la fecha de inicio +// 'lte' => $endDate, // Menor o igual a la fecha de fin +// ], +// 'limit' => $limit, // Establecer el límite de eventos por solicitud +// ]; + +// // Si ya se ha procesado una página, agregar el parámetro de paginación +// if ($lastEventId) { +// $params['starting_after'] = $lastEventId; +// } + +// // Obtener los eventos +// $events = $stripe->events->all($params); + +// // Filtrar por customer ID y almacenar los eventos filtrados +// foreach ($events->data as $event) { +// $eventObject = $event->data->object; + +// if (isset($eventObject->customer) && $eventObject->customer === $customerId) { +// $filteredEvents[] = $event; // Almacenar el evento filtrado +// } +// } + +// // Guardar el ID del último evento para la paginación +// if (!empty($events->data)) { +// $lastEventId = end($events->data)->id;// Obtener el último ID de evento +// } + +// } while (!empty($events->has_more)); // Repetir mientras haya más eventos + +// // Ordenar los eventos filtrados por fecha de creación (más reciente primero) +// usort($filteredEvents, function ($a, $b) { +// return $b->created - $a->created; // Orden descendente +// }); + +// // Obtener el evento más reciente +// if (!empty($filteredEvents)) { +// $mostRecentEvent = $filteredEvents[0]; // El primer evento será el más reciente +// $eventObject = $mostRecentEvent->data->object; + +// $netAmount = abs($eventObject->net_amount); // Valor absoluto de la cantidad en centavos +// $netAmountMXN = $netAmount / 100; // Convertir a pesos mexicanos + +// // echo "Evento ID: " . $mostRecentEvent->id . "\n"; +// // echo "Tipo de Evento: " . $mostRecentEvent->type . "\n"; +// // echo "Fecha del Evento: " . date('Y-m-d H:i:s', $mostRecentEvent->created) . "\n"; +// // echo "Cantidad en positivo: " . $netAmountMXN . " MXN\n"; // Mostrar la cantidad positiva convertida a MXN +// // echo "Saldo final: " . ($eventObject->ending_balance / 100) . " MXN\n"; // Mostrar el saldo convertido a MXN +// // echo "Detalles: " . json_encode($eventObject, JSON_PRETTY_PRINT) . "\n"; + +// $saldoFinal = ($eventObject->ending_balance / 100); // Asignar el saldo final +// } else { +// echo "No se encontraron eventos para el cliente especificado dentro del periodo dado.\n"; +// logMessage("No se encontraron eventos para el cliente especificado dentro del periodo dado.", 'error'); +// } + +// return $saldoFinal; +// } + + +function createPaymentIntent($stripe, $amount, $customer_id_stripe, $clientId_CRM, $signedAdminId) +{ + $amount = intval($amount * 100); //Conversión de la cantidad a centavos + try { + + $paymentIntent = $stripe->paymentIntents->create([ + 'amount' => $amount, //Cantidad a cobrar + 'currency' => 'mxn', + 'customer' => $customer_id_stripe, //ID del cliente en Stripe + 'payment_method_types' => ['customer_balance'], + 'payment_method_data' => ['type' => 'customer_balance'], + 'confirm' => true, + 'payment_method_options' => [ + 'customer_balance' => [ + 'funding_type' => 'bank_transfer', + 'bank_transfer' => ['type' => 'mx_bank_transfer'] + ], + ], + 'metadata' => [ + 'clientId' => $clientId_CRM, // ID del cliente en UISP CRM + 'createdBy' => 'UCRM', + 'paymentType' => 'card.one_time', + 'signedInAdminId' => $signedAdminId, // ID del administrador que realiza la operación + 'tipoPago' => 'Transferencia Bancaria' + ], + ]); + + print_r("PaymentIntent creado: " . $paymentIntent->id . PHP_EOL); //Imprime el ID del PaymentIntent creado + logMessage("PaymentIntent creado: " . $paymentIntent->id); //Imprime el ID del PaymentIntent creado + print_r("Status: " . $paymentIntent->status . PHP_EOL); //Imprime el estatus del PaymentIntent creado + logMessage("Status: " . $paymentIntent->status); //Imprime el estatus del PaymentIntent creado + print_r("Amount: " . $paymentIntent->amount . PHP_EOL); //Imprime el monto del PaymentIntent creado + logMessage("Amount: " . $paymentIntent->amount); //Imprime el monto del PaymentIntent creado + print_r("Currency: " . $paymentIntent->currency . PHP_EOL); //Imprime la moneda del PaymentIntent creado + logMessage("Currency: " . $paymentIntent->currency); //Imprime la moneda del PaymentIntent creado + print_r("Customer: " . $paymentIntent->customer . PHP_EOL); //Imprime el ID del cliente del PaymentIntent creado + logMessage("Customer: " . $paymentIntent->customer); //Imprime el ID del cliente del PaymentIntent creado + print_r("Payment Method: " . $paymentIntent->payment_method . PHP_EOL); //Imprime el ID del método de pago del PaymentIntent creado + logMessage("Payment Method: " . $paymentIntent->payment_method); //Imprime el ID del método de pago del PaymentIntent creado + print_r("Payment Method Type: " . $paymentIntent->payment_method_types[0] . PHP_EOL); //Imprime el tipo de método de pago del PaymentIntent creado + logMessage("Payment Method Type: " . $paymentIntent->payment_method_types[0]); //Imprime el tipo de método de pago del PaymentIntent creado + + + + } catch (\Stripe\Exception\ApiErrorException $e) { + print_r("Error creando PaymentIntent: " . $e->getMessage() . PHP_EOL); + logMessage("Error creando PaymentIntent: " . $e->getMessage() . PHP_EOL, 'error'); + } +} + + +// function createOxxoPaymentIntent($event_json, $amount = null): array +// { +// //declarar un array asociativo de strings pero no asignarle nada aun +// $arrayOxxoPayment = array(); +// $integerAmount = $amount; + +// print_r("Creando referencia del cliente para OXXO: " . PHP_EOL); +// $configManager = PluginConfigManager::create(); +// $config = $configManager->loadConfig(); +// $StripeToken = $config['tokenstripe']; +// $IPServer = $config['ipserver']; +// $tokenCRM = $config['apitoken']; + +// $baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; +// $ucrmApi = UcrmApi::create(); +// $currentUserAdmin = $ucrmApi->get('users/admins', []); +// $clientID = $event_json['client_id']; + +// $stripeCustomerId = null; +// $clientEmail = ''; +// $clientFullName = ''; + +// $clientGuzzleHttp = new Client([ +// 'base_uri' => $baseUri, +// 'headers' => [ +// 'X-Auth-App-Key' => $tokenCRM, +// 'Accept' => 'application/json', +// ], +// 'verify' => false, +// 'timeout' => 5, // Timeout de 5 segundos +// ]); + +// try { +// $response = $clientGuzzleHttp->request('GET', "clients/" . $clientID); +// $arrayClientCRM = json_decode($response->getBody()->getContents(), true); +// //$this->logger->info("Valor de arrayClientCRM: " . print_r($arrayClientCRM, true) . PHP_EOL); +// $clientFullName = $arrayClientCRM['firstName'] . ' ' . $arrayClientCRM['lastName']; +// } catch (RequestException $e) { +// if ($e->getCode() === 404) { +// print_r("Cliente no encontrado en CRM: " . $clientID . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID, clientFullName y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorGetClientNotFound'; +// $arrayOxxoPayment['failDescription'] = 'Cliente no encontrado en CRM: ' . $clientID; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $integerAmount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; + + +// } +// //timeout +// if ($e->getCode() === 408) { +// print_r("Timeout al obtener el cliente en CRM: " . $clientID . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorTimeoutGetClient'; +// $arrayOxxoPayment['failDescription'] = 'Timeout al obtener el cliente en CRM: ' . $clientID; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $integerAmount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; +// } +// print_r("Error al obtener el cliente en CRM (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorGetClient'; +// $arrayOxxoPayment['failDescription'] = 'Error al obtener el cliente en CRM: ' . $clientID; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $integerAmount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; + +// } + +// // Obtener email del cliente +// foreach ($arrayClientCRM['contacts'] as $contact) { +// if (!empty($contact['email'])) { +// $clientEmail = $contact['email']; +// break; +// } else { +// $clientEmail = 'siip8873@gmail.com'; // Default +// } +// } + +// try { +// // Obtener stripeCustomerId +// foreach ($arrayClientCRM['attributes'] as $attribute) { +// if ($attribute['key'] === 'stripeCustomerId') { +// $stripeCustomerId = $attribute['value']; +// break; +// } +// } + +// print_r("Stripe Customer ID obtenido: " . $stripeCustomerId . PHP_EOL); +// } catch (Exception $e) { +// print_r("Error al obtener el Customer ID de Stripe (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['error'] = 'errorGetCustomerStripe'; +// $arrayOxxoPayment['failDescription'] = 'Error al obtener el Customer ID de Stripe: ' . $clientID; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $integerAmount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; +// } + +// if ($amount === null) { +// $amount = abs($arrayClientCRM['accountOutstanding']); //Monto adeudado del cliente +// } else { +// print_r("Monto proporcionado directamente: $amount " . PHP_EOL); +// } + +// if ($amount > 10) { +// $amountInCents = intval($amount * 100); // Convertir a centavos + +// try { +// print_r("Creando referencia en Stripe por $amount para el cliente $stripeCustomerId" . PHP_EOL); +// $guzzleClient = new Client([ +// 'timeout' => 5, // Timeout de 5 segundos +// ]); + +// $response = $guzzleClient->post('https://api.stripe.com/v1/payment_intents', [ +// 'auth' => [$StripeToken, ''], +// 'form_params' => [ +// 'amount' => $amountInCents, +// 'currency' => 'mxn', +// 'payment_method_types' => ['customer_balance', 'card', 'oxxo'], +// 'description' => 'Pago de servicio de SIIP Internet', +// 'customer' => $stripeCustomerId, +// 'metadata' => [ +// 'clientId' => $clientID, +// 'createdBy' => 'UCRM', +// 'paymentType' => 'card.one_time', +// 'signedInAdminId' => $currentUserAdmin[1]['id'], +// 'tipoPago' => 'OXXO', +// ], +// 'payment_method_options' => [ +// 'oxxo' => [ +// 'expires_after_days' => 3, +// ], +// ], +// ] +// ]); + +// $paymentIntent = json_decode($response->getBody()->getContents(), true); + +// } catch (\Stripe\Exception\ApiConnectionException $e) { +// print_r("Error de conexión con Stripe (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorConnectionStripe'; +// $arrayOxxoPayment['failDescription'] = 'Error de conexión con Stripe: ' . $clientID; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $amount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; + +// } catch (\Stripe\Exception\ApiErrorException $e) { +// print_r("Error de la API de Stripe: " . $e->getMessage() . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorApiStripe'; +// $arrayOxxoPayment['failDescription'] = 'Error de la API de Stripe: ' . $clientID; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $amount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; + +// } catch (Exception $e) { +// print_r("Error inesperado al crear PaymentIntent (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); +// //si e->getMessage incluye el mensaje: You must provide a customer when creating or updating a PaymentIntent with a `customer_ba (truncated...) declarar una variable y ahi poner "Este cliente no tiene cuenta de Stripe" +// if ($e->getMessage() == 'You must provide a customer when creating or updating a PaymentIntent with a `customer_ba (truncated...)') { +// print_r("Este cliente no tiene cuenta de Stripe" . PHP_EOL); +// $arrayOxxoPayment['failDescription'] = 'Este cliente no tiene cuenta de Stripe: ' . $clientID; +// } else { +// print_r("Este cliente no tiene cuenta de Stripe" . PHP_EOL); +// print_r("Error inesperado al crear PaymentIntent: " . $e->getMessage() . PHP_EOL); +// $arrayOxxoPayment['failDescription'] = 'Error inesperado al crear PaymentIntent: ' . $clientID; +// } +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorCreatePaymentIntent'; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $amount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; + +// } + +// try { +// $firstName = isset($arrayClientCRM['firstName']) ? trim($arrayClientCRM['firstName']) : ''; +// $lastName = isset($arrayClientCRM['lastName']) ? trim($arrayClientCRM['lastName']) : ''; + +// if (strlen($firstName) < 2 || strlen($lastName) < 2) { +// print_r("Este cliente no tiene cuenta de Stripe" . PHP_EOL); +// ("Nombre/apellido inválido: ' . $firstName . ' ' . $lastName" . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorNombreApellidoInvalido'; +// $arrayOxxoPayment['failDescription'] = "Nombre/apellido inválido: ' . $firstName . ' ' . $lastName"; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $amount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; + +// } + +// $responsePaymentMethod = $guzzleClient->post('https://api.stripe.com/v1/payment_methods', [ +// 'auth' => [$StripeToken, ''], +// 'form_params' => [ +// 'type' => 'oxxo', +// 'billing_details' => [ +// 'name' => "$firstName $lastName", +// 'email' => $clientEmail, +// ], +// ] +// ]); + +// $paymentMethod = json_decode($responsePaymentMethod->getBody()->getContents(), true); + +// $responseConfirmPaymentIntent = $guzzleClient->post('https://api.stripe.com/v1/payment_intents/' . $paymentIntent['id'] . '/confirm', [ +// 'auth' => [$StripeToken, ''], +// 'form_params' => [ +// 'payment_method' => $paymentMethod['id'], +// ] +// ]); + +// $paymentIntentConfirm = json_decode($responseConfirmPaymentIntent->getBody()->getContents(), true); + +// } catch (Exception $e) { +// print_r("Error al confirmar PaymentIntent: " . $e->getMessage() . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorConfirmPaymentIntent'; +// $arrayOxxoPayment['failDescription'] = 'Error al confirmar PaymentIntent: ' . $e->getMessage(); +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $amount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; +// } + +// if (!empty($paymentIntentConfirm['next_action']) && isset($paymentIntentConfirm['next_action']['oxxo_display_details'])) { +// $oxxoPayment = $paymentIntentConfirm['next_action']['oxxo_display_details']; +// $oxxo_reference = $oxxoPayment['number']; +// $oxxo_receipt_url = $oxxoPayment['hosted_voucher_url']; + +// print_r("Referencia OXXO: " . $oxxo_reference . PHP_EOL); +// print_r("URL del recibo: " . $oxxo_receipt_url . PHP_EOL); +// //$this->captureScreenshot($oxxo_receipt_url); +// //devolver un array con los campos de url de oxxo, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = $oxxo_reference; +// $arrayOxxoPayment['url'] = $oxxo_receipt_url; +// $arrayOxxoPayment['error'] = ''; +// $arrayOxxoPayment['failDescription'] = ''; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $amount; +// print_r("Referencia OXXO creada correctamente." . PHP_EOL); + + + +// return $arrayOxxoPayment; +// } else { +// print_r("El PaymentIntent no tiene detalles de OXXO disponibles. Estado: " . $paymentIntentConfirm['status'] . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorPaymentIntentWithoutOxxoDetails'; +// $arrayOxxoPayment['failDescription'] = 'El PaymentIntent no tiene detalles de OXXO disponibles. Estado: ' . $paymentIntentConfirm['status']; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $amount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; + +// } + +// } else { +// print_r("Este cliente no tiene adeudos." . PHP_EOL); +// //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount +// $arrayOxxoPayment['oxxo_reference'] = ''; +// $arrayOxxoPayment['url'] = ''; +// $arrayOxxoPayment['error'] = 'errorsinadeudo'; +// $arrayOxxoPayment['failDescription'] = 'Este cliente no tiene adeudos.'; +// $arrayOxxoPayment['clientID'] = $clientID; +// $arrayOxxoPayment['clientFullName'] = $clientFullName; +// $arrayOxxoPayment['amount'] = $amount; +// $arrayOxxoPayment['voucher_image_url'] = ''; +// return $arrayOxxoPayment; +// } +// } + + +// /** +// * Cancela todos los PaymentIntents en estado 'requires_action' para un cliente específico. +// * +// * @param \Stripe\StripeClient $stripe Instancia de StripeClient +// * @param string $customerId ID del cliente en Stripe +// * @return bool True si se encontraron y cancelaron PaymentIntents, False si no hay ninguno en estado 'requires_action' +// */ +// function cancelRequiresActionPaymentIntents($stripe, $customerId) +// { +// try { +// // Listar todos los PaymentIntents del cliente +// $paymentIntents = $stripe->paymentIntents->all([ +// 'customer' => $customerId, +// 'limit' => 100, // Número máximo de resultados por página +// ]); + +// $foundAndCanceled = false; + +// foreach ($paymentIntents->data as $intent) { +// // Verificar si el estado es 'requires_action' +// if ($intent->status === 'requires_action') { +// // Cancelar el PaymentIntent +// $stripe->paymentIntents->cancel($intent->id); +// $foundAndCanceled = true; +// } +// } + +// // Manejar paginación si hay más de 100 PaymentIntents +// while ($paymentIntents->has_more) { +// $lastIntentId = end($paymentIntents->data)->id; +// $paymentIntents = $stripe->paymentIntents->all([ +// 'customer' => $customerId, +// 'limit' => 100, +// 'starting_after' => $lastIntentId, +// ]); + +// foreach ($paymentIntents->data as $intent) { +// if ($intent->status === 'requires_action') { +// $stripe->paymentIntents->cancel($intent->id); +// $foundAndCanceled = true; +// } +// } +// } + +// return $foundAndCanceled; + +// } catch (\Stripe\Exception\ApiErrorException $e) { +// // Manejo de errores +// error_log('Error al procesar PaymentIntents: ' . $e->getMessage()); +// return false; +// } +// } +function logMessage($message, $type = 'info') +{ + $logFile = __DIR__ . '/payment_intents.log'; + $dateTime = new DateTime('now', new DateTimeZone('America/Mexico_City')); + $formattedDateTime = $dateTime->format('Y-m-d H:i:s'); + file_put_contents($logFile, "[$formattedDateTime] [$type] $message" . PHP_EOL, FILE_APPEND); +} diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 9fa52ea..71b77b4 --- a/README.md +++ b/README.md @@ -38,5 +38,5 @@ Este plugin permite buscar clientes en la base de datos de UISP CRM y generar in - Si el cliente no tiene un "Stripe Customer ID", no se permitirá generar el pago. --- -**Versión**: 1.1.0 +**Versión**: 1.2.0 **Copyright**: © 2024 SIIP Internet. Todos los derechos reservados. diff --git a/archivos_webhook_destino/main.php b/archivos_webhook_destino/main.php new file mode 100755 index 0000000..a1c4aa7 --- /dev/null +++ b/archivos_webhook_destino/main.php @@ -0,0 +1,23 @@ +build(); + $plugin = $container->get(\SmsNotifier\Plugin::class); + + try { + $plugin->run(); + + // cleanup plugin log + //$container->get(\SmsNotifier\Service\LogCleaner::class)->clean(); + } catch (Exception $e) { + $logger = new \SmsNotifier\Service\Logger(); + $logger->error($e->getMessage()); + $logger->debug($e->getTraceAsString()); + } +})(); +http_response_code(200); //Response OK when it's called by a webhook. diff --git a/archivos_webhook_destino/public.php b/archivos_webhook_destino/public.php new file mode 100755 index 0000000..b449224 --- /dev/null +++ b/archivos_webhook_destino/public.php @@ -0,0 +1,147 @@ + +// +// Archivos PDF +// Resultado +// +// '; + +// borrarArchivosPDFWordpress(); +// echo ' +// '; +// } + +// function borrarArchivosPDFWordpress() +// { +// $log = PluginLogManager::create(); //Initialize Logger +// $configManager = PluginConfigManager::create(); +// $config = $configManager->loadConfig(); + +// // Configuración de conexión FTP +// $ftp_server = $config['hostServerFTP']; +// $ftp_username = $config['usernameServerFTP']; +// $ftp_password = $config['passServerFTP']; +// $remote_folder = "/public_html/wp/wp-content/uploads/img/"; + +// // Conexión FTP +// $ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor FTP"); +// $login = ftp_login($ftp_conn, $ftp_username, $ftp_password); +// ftp_pasv($ftp_conn, true); + +// // Verificar conexión y login +// if ($ftp_conn && $login) { +// $log->appendLog("Conexión FTP exitosa" . PHP_EOL); + +// // Obtener lista de archivos en la carpeta +// $files = ftp_nlist($ftp_conn, $remote_folder); +// if (is_array($files)) { +// // Eliminar la ruta del directorio de los archivos +// $files = array_map(function($file) use ($remote_folder) { +// return str_replace($remote_folder, '', $file); +// }, $files); + +// // Obtener fechas de modificación +// $filesWithTime = []; +// foreach ($files as $file) { +// $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); +// if ($modifiedTime != -1) { +// $filesWithTime[$file] = $modifiedTime; +// } +// } + +// // Ordenar archivos por fecha de modificación, más recientes primero +// arsort($filesWithTime); + +// // Obtener los archivos a eliminar (todos menos los 5 más recientes) +// $filesToDelete = array_slice(array_keys($filesWithTime), 5); + +// // Eliminar archivos antiguos +// foreach ($filesToDelete as $file) { +// if (ftp_delete($ftp_conn, $remote_folder . $file)) { +// echo '' . $file . 'Archivo borrado'; +// } else { +// echo '' . $file . 'Error al borrar archivo'; +// } +// } +// $log->appendLog("Archivos eliminados" . PHP_EOL); +// } else { +// $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); +// } +// // Cerrar conexión FTP +// ftp_close($ftp_conn); +// } else { +// $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); +// } +// } + +// // Verificar si se ha enviado una solicitud POST +// if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST["pintar"])) { +// // Llamar a la función para imprimir la tabla +// imprimirTabla(); + +// } + +// // Contenido HTML para el formulario con el botón +// $html = ' +// +// +// +// +// +// +// +// +//

Limpieza de archivos PDF de comprobantes de pago subidos a Wordpress para su envío

+//
+//
+// +//
+//
+ +// +// +// '; +// echo $html; diff --git a/archivos_webhook_destino/src/Facade/AbstractMessageNotifierFacade.php b/archivos_webhook_destino/src/Facade/AbstractMessageNotifierFacade.php new file mode 100755 index 0000000..0e5a4d6 --- /dev/null +++ b/archivos_webhook_destino/src/Facade/AbstractMessageNotifierFacade.php @@ -0,0 +1,1873 @@ +logger = $logger; + $this->messageTextFactory = $messageTextFactory; + $this->clientPhoneNumber = $clientPhoneNumber; + + } + + /* + * Verify contact type numbers to do notification, update or both + */ + public function verifyPaymentActionToDo(NotificationData $notificationData): void + { + + //$this->logger->debug(print_r(json_encode($notificationData),true).PHP_EOL); + + // Obtener los números de teléfono de los contactos + $arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null); + + // Imprimir el array de teléfonos + $this->logger->info(json_encode($arrayPhones)); + + // Procesar el array de teléfonos y ejecutar la función correspondiente + foreach ($arrayPhones as $type => $phones) { + + // Registrar el tipo de contacto antes de la normalización para verificar qué valor se recibe + $this->logger->info("Procesando tipo de contacto original: '$type'"); + + // Normalizar el tipo para manejar posibles diferencias en las mayúsculas o espacios en blanco + $type = trim(strtolower($type)); + + // Registrar el tipo de contacto después de la normalización + $this->logger->info("Tipo de contacto normalizado: '$type'"); + + // Asegurarse de que $phones es un array y recorrerlo + if (is_array($phones)) { + foreach ($phones as $phone) { + switch ($type) { + case 'whatsapp': + // Ejecutar función de notificación y actualización + $this->notifyAndUpdate($notificationData, $phone); + break; + + case 'whatsnotifica': + // Ejecutar función de notificación + $this->notify($notificationData, $phone); + break; + + case 'whatsactualiza': + // Ejecutar función de actualización + $this->onlyUpdate($notificationData, $phone, false); + break; + + default: + // Registrar cuando el tipo no es reconocido + $this->logger->info("Tipo de contacto no reconocido: '$type'" . PHP_EOL); + break; + } + } + } else { + $this->logger->info("Formato no esperado para los números de teléfono en el tipo: $type" . PHP_EOL); + } + } + + } + + /* + * Verify contact type numbers to do client's data update at CallBell + */ + public function verifyClientActionToDo(NotificationData $notificationData): void + { + //$this->logger->debug("Verificar contactos" . PHP_EOL); + + $this->logger->debug(print_r(json_encode($notificationData), true) . PHP_EOL); + + $arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null); + + // Procesar el array de teléfonos y ejecutar la función correspondiente + foreach ($arrayPhones as $type => $phones) { + + // Registrar el tipo de contacto antes de la normalización para verificar qué valor se recibe + $this->logger->info("Procesando tipo de contacto original: '$type'"); + + // Normalizar el tipo para manejar posibles diferencias en las mayúsculas o espacios en blanco + $type = trim(strtolower($type)); + + // Registrar el tipo de contacto después de la normalización + $this->logger->info("Tipo de contacto normalizado: '$type'"); + + // Asegurarse de que $phones es un array y recorrerlo + if (is_array($phones)) { + foreach ($phones as $phone) { + switch ($type) { + case 'whatsapp': + // Ejecutar función de notificación y actualización + $this->onlyUpdate($notificationData, $phone); + break; + + case 'whatsactualiza': + // Ejecutar función de actualización + $this->onlyUpdate($notificationData, $phone); + break; + + default: + // Registrar cuando el tipo no es reconocido + $this->logger->info("Tipo de contacto no reconocido: '$type'" . PHP_EOL); + break; + } + } + } else { + $this->logger->info("Formato no esperado para los números de teléfono en el tipo: $type" . PHP_EOL); + } + } + + } + /* + * Verify contact type numbers to do client's data service status at CallBell + */ + public function verifyServiceActionToDo(NotificationData $notificationData): void + { + + //$this->logger->debug(print_r(json_encode($notificationData),true).PHP_EOL); + + $arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null); + + // Procesar el array de teléfonos y ejecutar la función correspondiente + foreach ($arrayPhones as $type => $phones) { + + // Registrar el tipo de contacto antes de la normalización para verificar qué valor se recibe + $this->logger->info("Procesando tipo de contacto original: '$type'"); + + // Normalizar el tipo para manejar posibles diferencias en las mayúsculas o espacios en blanco + $type = trim(strtolower($type)); + + // Registrar el tipo de contacto después de la normalización + $this->logger->info("Tipo de contacto normalizado: '$type'"); + + // Asegurarse de que $phones es un array y recorrerlo + if (is_array($phones)) { + foreach ($phones as $phone) { + switch ($type) { + case 'whatsapp': + // Ejecutar función de notificación y actualización + $this->onlyUpdateService($notificationData, $phone); + break; + + case 'whatsactualiza': + // Ejecutar función de actualización + $this->onlyUpdateService($notificationData, $phone); + break; + + default: + // Registrar cuando el tipo no es reconocido + $this->logger->info("Tipo de contacto no reconocido: '$type'" . PHP_EOL); + break; + } + } + } else { + $this->logger->info("Formato no esperado para los números de teléfono en el tipo: $type" . PHP_EOL); + } + } + + } + + /* + * Verify contact type numbers to do client's + */ + public function verifyJobActionToDo($jsonNotificationData, $reprogramming = null, $changeInstaller = null): void + { + + $this->logger->debug('******** INCIO DE LA FUNCIÓN verifyJobActionToDo ******' . PHP_EOL); + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + $IPServer = $config['ipserver']; + $UCRMAPIToken = $config['apitoken']; + $CallBellAPIToken = $config['tokencallbell']; + $IPServer = $config['ipserver']; + $installersData = $config['installersDataWhatsApp']; + + $this->logger->debug('Valor de $jsonNotificationData en verifyJobActionToDo: ' . json_encode($jsonNotificationData) . PHP_EOL); + + $clientId = $jsonNotificationData['extraData']['entity']['clientId']; + $installerId = $jsonNotificationData['extraData']['entity']['assignedUserId']; + $jobId = $jsonNotificationData['entityId']; + $jobDescription = $jsonNotificationData['extraData']['entity']['description'] ?? null; + + $reprogrammingValue = var_export($reprogramming, true); + $changeInstallerValue = var_export($changeInstaller, true); + $this->logger->debug('Valor de $reprogramming en verifyJobActionToDo: ' . $reprogrammingValue . PHP_EOL); + $this->logger->debug('Valor de $changeInstaller en verifyJobActionToDo: ' . $changeInstallerValue . PHP_EOL); + + // Obtener la fecha del nodo "entity" + $dateString = $jsonNotificationData['extraData']['entity']['date'] ?? null; + + if ($dateString) { + // Crear un objeto DateTime a partir de la fecha + $date = new DateTime($dateString); + + + // concaternar emoji de reloj de la hora que más se asemeje a la hora del evento, por ejemplo si la hora es 8:00 am o pm se concatena el emoji de reloj de las 8:00 am o pm como: 🕗 + $hour = $date->format('h'); + $clockEmoji = "🕐"; // Emoji por defecto + // Determinar el emoji del reloj según la hora + // switch ($hour) { + // case '01': + // $clockEmoji = "🕐"; + // break; + // case '02': + // $clockEmoji = "🕑"; + // break; + // case '03': + // $clockEmoji = "🕒"; + // break; + // case '04': + // $clockEmoji = "🕓"; + // break; + // case '05': + // $clockEmoji = "🕔"; + // break; + // case '06': + // $clockEmoji = "🕕"; + // break; + // case '07': + // $clockEmoji = "🕖"; + // break; + // case '08': + // $clockEmoji = "🕗"; + // break; + // case '09': + // $clockEmoji = "🕘"; + // break; + // case '10': + // $clockEmoji = "🕙"; + // break; + // case '11': + // $clockEmoji = "🕚"; + // break; + // case '12': + // $clockEmoji = "🕛"; + // break; + // } + + // Formatear la fecha al formato deseado: día/mes/año a las hora:minuto am/pm aproximadamente + // $formattedDate = "*" . $date->format('d/m/Y') . "* a las $clockEmoji *" . $date->format('h:i A') . "* aproximadamente"; + $formattedDate = sprintf("*%s*", $date->format('d/m/Y')); + + + // $this->logger->debug('Valor de $formattedDate en verifyJobActionToDo: ' . $formattedDate . PHP_EOL); + } else { + $this->logger->error('La fecha no está disponible en el JSON' . PHP_EOL); + } + + $this->logger->debug('Valor de $installersData en verifyJobActionToDo: ' . $installersData . PHP_EOL); + + $this->ucrmApi = UcrmApi::create(); + $usersInstallers = $this->ucrmApi->get('users/admins/' . $installerId, []); + $this->logger->debug('Valor de $usersInstallers en verifyJobActionToDo: ' . json_encode($usersInstallers) . PHP_EOL); + + // Inicializar la variable para el nombre completo + $installerFullName = ''; + + $jsonInstallersData = json_decode($installersData, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + $this->logger->error('Error al decodificar el JSON de instaladores: ' . json_last_error_msg()); + return; + } + + // Verificar si el nodo "instaladores" existe + if (!isset($jsonInstallersData['instaladores'])) { + $this->logger->error('El nodo "instaladores" no existe en el JSON'); + return; + } + + // Buscar el número de WhatsApp en el JSON + $installerWhatsApp = ''; + foreach ($jsonInstallersData['instaladores'] as $installer) { + if ($installer['id'] === $installerId) { + $installerWhatsApp = $installer['whatsapp']; + break; + } + } + //$this->logger->debug('tipo de dato de installerWhatsApp: ' . gettype($installerWhatsApp) . PHP_EOL); + + // Validar si se encontró el WhatsApp + if (empty($installerWhatsApp)) { + $this->logger->warning('No se encontró un número de WhatsApp para el instalador con ID 1019'); + } else { + $this->logger->debug('Número de WhatsApp del Instalador: ' . $installerWhatsApp . PHP_EOL); + } + + $this->ucrmApi = UcrmApi::create(); + $usersInstallers = $this->ucrmApi->get('users/admins/' . $installerId, []); + //$this->logger->debug('Valor de $usersInstallers ' . json_encode($usersInstallers) . PHP_EOL); + + $firstName = $usersInstallers['firstName'] ?? ''; + $lastName = $usersInstallers['lastName'] ?? ''; + $installerFullName = trim("$firstName $lastName"); + $this->logger->debug('Valor de $installerFullName: ' . $installerFullName . PHP_EOL); + + $baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; + + $clientGuzzleHttp = new Client([ + 'base_uri' => $baseUri, + 'headers' => [ + 'X-Auth-App-Key' => $UCRMAPIToken, + 'Accept' => 'application/json', + ], + 'verify' => false, + ]); + + $responseClients = $clientGuzzleHttp->request('GET', "clients/" . $clientId); + $arrayClientCRM = json_decode($responseClients->getBody()->getContents(), true); + // $this->logger->debug('Valor de $arrayClientCRM en verifyJobActionToDo: ' . json_encode($arrayClientCRM) . PHP_EOL); + //ejemplo de $arrayClientCRM: {"id":2,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"Chiapas 31","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"Chiapas 31, Dolores Hidalgo Cuna de la Independencia Nacional, 37800","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":"Cliente espacial, el m\u00e1s chido","sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2024-01-25T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":true,"firstName":"Daniel Humberto","lastName":"Soto Villegas","username":"danydhsv","contacts":[{"id":2,"clientId":2,"email":"dhsv.141089@gmail.com","phone":"5214181878106","name":null,"isBilling":false,"isContact":false,"types":[]},{"id":170,"clientId":2,"email":null,"phone":"5214181148783","name":null,"isBilling":false,"isContact":false,"types":[{"id":1000,"name":"WhatsApp"}]}],"attributes":[{"id":112,"clientId":2,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_PetN1dhr4rx0kX","clientZoneVisible":true},{"id":113,"clientId":2,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"0021804341999569810","clientZoneVisible":true},{"id":178,"clientId":2,"customAttributeId":15,"name":"Site","key":"site","value":"0LOCS","clientZoneVisible":false},{"id":179,"clientId":2,"customAttributeId":16,"name":"Antena\/Sectorial","key":"antenaSectorial","value":"Sectorial-4b 172.16.13.16\/24","clientZoneVisible":false},{"id":200,"clientId":2,"customAttributeId":14,"name":"Chat de CallBell","key":"chatDeCallbell","value":"https","clientZoneVisible":false},{"id":202,"clientId":2,"customAttributeId":17,"name":"Password Antena Cliente","key":"passwordAntenaCliente","value":"gYAIEK:Be}SK*01z5+\/V","clientZoneVisible":false}],"accountBalance":-200,"accountCredit":100,"accountOutstanding":300,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#f1df43","addressGpsLat":21.1564414,"addressGpsLon":-100.9383654,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":true,"hasSuspendedService":false,"hasServiceWithoutDevices":true,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false} + + //buscar en los attributes del cliente el atributo de con la key "passwordAntenaCliente" y almacenar su valor en la variable $passwordAntenaClienteCRM + $passwordAntenaClienteCRM = ''; + foreach ($arrayClientCRM['attributes'] as $attribute) { + if ($attribute['key'] === 'passwordAntenaCliente') { + $passwordAntenaClienteCRM = $attribute['value']; + $this->logger->debug('Valor de $passwordAntenaClienteCRM en verifyJobActionToDo: ' . $passwordAntenaClienteCRM . PHP_EOL); + break; + } + } + + $clientFullName = sprintf("%s %s", $arrayClientCRM['firstName'], $arrayClientCRM['lastName']); + + // Extraer la dirección completa + $fullAddress = $arrayClientCRM['fullAddress'] ?? null; + + // Validar si la dirección completa está disponible + if ($fullAddress) { + // Convertir la dirección a HTML seguro + $safeAddress = htmlspecialchars($fullAddress, ENT_QUOTES, 'UTF-8'); + // $this->logger->debug('Dirección completa: ' . $safeAddress . PHP_EOL); + } else { + $this->logger->error('La dirección completa no está disponible en el JSON' . PHP_EOL); + } + + // Extraer las coordenadas del nodo "entity" + $gpsLat = $arrayClientCRM['addressGpsLat'] ?? null; + $gpsLon = $arrayClientCRM['addressGpsLon'] ?? null; + + // Validar si las coordenadas están disponibles + if ($gpsLat && $gpsLon) { + // Construir la URL de Google Maps + $googleMapsUrl = sprintf('https://www.google.com/maps?q=%s,%s', $gpsLat, $gpsLon); + // $this->logger->debug('URL de Google Maps: ' . $googleMapsUrl . PHP_EOL); + } else { + $googleMapsUrl = 'Las coordenadas no están disponibles en este cliente.'; + $this->logger->error('Las coordenadas no están disponibles en el JSON' . PHP_EOL); + } + + $arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers(null, $arrayClientCRM); + // $this->logger->debug('Valor de $arrayPhones en verifyJobActionToDo: ' . json_encode($arrayPhones) . PHP_EOL); + + $arrayAllPhones = $this->clientPhoneNumber->getAllUcrmClientNumbers($arrayClientCRM); + + // Convertir el array de teléfonos en una cadena separada por comas + $clientAllPhonesString = implode(',', $arrayAllPhones); + $this->logger->debug('Valor de $clientAllPhonesString en verifyJobActionToDo: ' . $clientAllPhonesString . PHP_EOL); + + // Dividir los números en un array + $arrayNumeros = explode(',', $clientAllPhonesString); + $this->logger->debug('Valor de $arrayNumeros en verifyJobActionToDo: ' . json_encode($arrayNumeros) . PHP_EOL); + + // Procesar cada número + $resultados = []; + foreach ($arrayNumeros as $numero) { + $numero = trim($numero); + $numeroValidado = $this->validarNumeroTelefono($numero); + + // Asegurar que solo quedan números + $numeroValidado = preg_replace('/\D/', '', $numeroValidado); + + // Si empieza con 521 y tiene más de 12 dígitos, lo quitamos + if (substr($numeroValidado, 0, 3) === "521") { + $resultados[] = substr($numeroValidado, 3); + } else { + $resultados[] = $numeroValidado; + } + } + $this->logger->debug('Valor de $resultados en verifyJobActionToDo: ' . json_encode($resultados) . PHP_EOL); + + // Convertir el array de resultados en una cadena separada por comas + $resultadoFinalNumerosCliente = implode(', ', $resultados); + $this->logger->debug('Valor de $resultadoFinalNumerosCliente en verifyJobActionToDo: ' . $resultadoFinalNumerosCliente . PHP_EOL); + + $client_callbell_api = new ClientCallBellAPI($UCRMAPIToken, $IPServer, $CallBellAPIToken); + + // Construir el array asociativo con los datos de la notificación para el cliente + $jsonClientJobNotificationData = [ + "clientFullName" => $clientFullName, + "jobId" => $jobId, + "date" => $formattedDate, + "installerName" => $installerFullName, + ]; + + $clientWhatsApp = ''; + + //Parte de la notificación al cliente + // Procesar el array de teléfonos y ejecutar la función correspondiente + foreach ($arrayPhones as $type => $phones) { + + // Normalizar el tipo para manejar posibles diferencias en las mayúsculas o espacios en blanco + $type = trim(strtolower($type)); + + switch ($type) { + case 'whatsapp': + case 'whatsnotifica': + $this->logger->debug("Se encontró un tipo de contacto $type" . PHP_EOL); + + // Verificar si el valor es un array de teléfonos + if (is_array($phones)) { + $attempts = 0; + $maxAttempts = 3; + $result = false; + foreach ($phones as $phone) { + + //Aquí se enviará la notificación al cliente + // Reintentar hasta 3 veces si la función retorna false + while ($attempts < $maxAttempts && $result === false) { + $attempts++; + $result = $client_callbell_api->sendJobNotificationWhatsAppToClient( + $this->validarNumeroTelefono($phone), // Validar número de teléfono + $jsonClientJobNotificationData, // Datos de notificación + $reprogramming, // Reprogramación + $changeInstaller // Cambio de instalador + ); + + if ($result === false) { + sleep(1); // Esperar 1 segundo antes de reintentar + $this->logger->warning("Intento $attempts fallido en el envío de notificación a $phone. Reintentando..." . PHP_EOL); + } + } + + // Verificar el resultado final + if ($result === true) { + $this->logger->info("Notificación enviada correctamente al cliente con teléfono $phone después de $attempts intento(s)." . PHP_EOL); + $title = $jsonNotificationData['extraData']['entity']['title']; + $this->logger->debug('Valor de $title en verifyJobActionToDo: ' . $title . PHP_EOL); + //la variable $title contiene el título del trabajo con un prefijo "[NOTIFICACION-PENDIENTE]" aquí hay que quitarlo + + // $this->ucrmApi = UcrmApi::create(); + // $responsePatch = $this->ucrmApi->patch('scheduling/jobs/' . $jsonNotificationData['entityId'], [ + // 'title' => $title, + // ]); + if (strpos($title, '[NOTIFICACION-PENDIENTE]') !== false) { //si el título contiene el prefijo [NOTIFICACION-PENDIENTE] entonces se quita + $title = str_replace('[NOTIFICACION-PENDIENTE]', '', $title); // Quitar el prefijo + $responseClients = $clientGuzzleHttp->request('PATCH', "scheduling/jobs/" . $jsonNotificationData['entityId'], [ + 'json' => [ + 'title' => $title, + ], + ]); + $this->logger->debug('Valor de $title después de quitar el prefijo en verifyJobActionToDo: ' . $title . PHP_EOL); + break; + + } else { + $this->logger->debug('El título no contiene el prefijo [NOTIFICACION-PENDIENTE] en verifyJobActionToDo: ' . $title . PHP_EOL); + } + + } else { + $this->logger->error("No se pudo enviar la notificación al cliente con teléfono $phone después de $maxAttempts intentos." . PHP_EOL); + } + } + } else { + $this->logger->warning("No se encontraron números de teléfono para el tipo de contacto $type." . PHP_EOL); + } + + break; + + default: + $this->logger->info("Tipo de contacto no reconocido: $type" . PHP_EOL); + break; + } + } + + // --- ¡AÑADE ESTAS LÍNEAS PARA CONVERTIR A BOOLEANO REAL! --- + $reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + $changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + // ----------------------------------------------------------- + + // Puedes volver a loggear los valores para confirmar la conversión (opcional) + $this->logger->debug("DEBUG: Valores después de conversión para mensaje instalador - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL); + + + if ($changeInstaller) { //Si se cambió el instalador + $this->logger->debug('Se cambió el instalador, por lo tanto se procede a enviarle mensaje al que se le desasignó' . PHP_EOL); + + // Construir el array asociativo con los datos de la notificación + + //Enviar notificación al instalador anterior + + $previousinstallerInstallerId = $installerId = $jsonNotificationData['extraData']['entityBeforeEdit']['assignedUserId']; + $usersInstallers = $this->ucrmApi->get('users/admins/' . $previousinstallerInstallerId, []); + $firstNamePreviousInstaller = $usersInstallers['firstName'] ?? ''; //Obtener el nombre del instalador anterior + $lastNamePreviousInstaller = $usersInstallers['lastName'] ?? ''; //Obtener el apellido del instalador anterior + $previousInstallerFullName = trim("$firstNamePreviousInstaller $lastNamePreviousInstaller"); + $attempts = 0; + $maxAttempts = 3; + $result = false; + + $jsonPreviousInstallerJobNotificationData = [ + "installerName" => "👷🏻‍♂️" . $previousInstallerFullName, + "subjectOfChange" => self::SUBJECT_OF_INSTALLER_CHANGE[1], + "jobId" => $jobId, + "clientFullName" => sprintf("[%s] %s", $arrayClientCRM['id'], $clientFullName), + "additionalChangeData" => self::ADDITIONAL_CHANGE_DATA[1] . ' *' . $installerFullName . '*', + + ]; + + // Buscar el número de WhatsApp en el JSON del instalador anterior + $previousInstallerWhatsApp = ''; + foreach ($jsonInstallersData['instaladores'] as $installer) { + if ($installer['id'] === $previousinstallerInstallerId) { + $previousInstallerWhatsApp = $installer['whatsapp']; + $this->logger->debug('Se encontró el Whatsapp del instalador anterior en el JSON y es: ' . $previousInstallerWhatsApp . PHP_EOL); + break; + } + } + + //Enviar mensaje al instalador anterior + // Reintentar hasta 3 veces si la función retorna false + while ($attempts < $maxAttempts && $result === false) { + $attempts++; + $result = $client_callbell_api->sendJobNotificationWhatsAppToInstaller( + $this->validarNumeroTelefono((string) $previousInstallerWhatsApp), + $jsonPreviousInstallerJobNotificationData, + $reprogramming, + $changeInstaller, + '' + + ); + + if ($result === false) { + sleep(1); + $this->logger->warning("Intento $attempts fallido para enviar notificación a $phone. Reintentando..." . PHP_EOL); + } + } + + sleep(4); + + // Construir el array asociativo con los datos de la notificación para el instalador nuevo + $jsonInstallerJobNotificationData = [ + "installerName" => $installerFullName, + "clientFullName" => sprintf("%s [ID:%s]", $clientFullName, $arrayClientCRM['id']), + "jobId" => $jobId, + "clientAddress" => $safeAddress, + "clientWhatsApp" => $resultadoFinalNumerosCliente, + "date" => $formattedDate, + "jobDescription" => $jobDescription, + "gmapsLocation" => $googleMapsUrl, + "passwordAntenaCliente" => '' + + ]; + + //obtener la contraseña de la antena con el método getVaultCredentialsByClientId y compararlo con el campo de la contraseña de la antena en el CRM + $passwordVault = $this->getVaultCredentialsByClientId($arrayClientCRM['id']); + + //Comparar las contraseñas y obtener la que se enviará al instalador + $jsonInstallerJobNotificationData['passwordAntenaCliente'] = $this->comparePasswords($passwordAntenaClienteCRM, $passwordVault); + + + + //Enviar notificación al instalador nuevo + + $attempts = 0; + $maxAttempts = 3; + $result = false; + + // Reintentar hasta 3 veces si la función retorna false + while ($attempts < $maxAttempts && $result === false) { + $attempts++; + $result = $client_callbell_api->sendJobNotificationWhatsAppToInstaller( + $this->validarNumeroTelefono((string) $installerWhatsApp), + $jsonInstallerJobNotificationData, + false, + false, + + ); + + if ($result === false) { + sleep(1); + $this->logger->warning("Intento $attempts fallido para enviar notificación a $phone. Reintentando..." . PHP_EOL); + } + } + + } else { //si el instalador no cambió + $this->logger->debug('No se cambió el instalador' . PHP_EOL); + + // Construir el array asociativo con los datos de la notificación + $jsonInstallerJobNotificationData = [ + "installerName" => $installerFullName, + "clientFullName" => sprintf("%s [ID:%s]", $clientFullName, $arrayClientCRM['id']), + "jobId" => $jobId, + "clientAddress" => $safeAddress, + "clientWhatsApp" => $resultadoFinalNumerosCliente, + "date" => $formattedDate, + "jobDescription" => $jobDescription, + "gmapsLocation" => $googleMapsUrl, + "passwordAntenaCliente" => '' + + ]; + + //obtener la contraseña de la antena con el método getVaultCredentialsByClientId y compararlo con el campo de la contraseña de la antena en el CRM + $passwordVault = $this->getVaultCredentialsByClientId($arrayClientCRM['id']); + + //Comparar las contraseñas y obtener la que se enviará al instalador + $jsonInstallerJobNotificationData['passwordAntenaCliente'] = $this->comparePasswords($passwordAntenaClienteCRM, $passwordVault); + + + + + $attempts = 0; + $maxAttempts = 3; + $result = false; + + // // Reintentar hasta 3 veces si la función retorna false + while ($attempts < $maxAttempts && $result === false) { + $attempts++; + $result = $client_callbell_api->sendJobNotificationWhatsAppToInstaller( + $this->validarNumeroTelefono((string) $installerWhatsApp), + $jsonInstallerJobNotificationData, + $reprogramming, + $changeInstaller, + + ); + + if ($result === false) { + sleep(1); + $this->logger->warning("Intento $attempts fallido para enviar notificación a $phone. Reintentando..." . PHP_EOL); + } + } + + } + + } + + /* + * Verify contact type numbers to do client's data update at CallBell by invoice add or edit + */ + public function verifyInvoiceActionToDo(NotificationData $notificationData): void + { + + $accountBalance = $notificationData->clientData['accountBalance']; + $this->logger->debug(print_r(json_encode($notificationData), true) . PHP_EOL); + + $arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null); + + // Procesar el array de teléfonos y ejecutar la función correspondiente + foreach ($arrayPhones as $type => $phones) { + + // Registrar el tipo de contacto antes de la normalización para verificar qué valor se recibe + $this->logger->info("Procesando tipo de contacto original: '$type'"); + + // Normalizar el tipo para manejar posibles diferencias en las mayúsculas o espacios en blanco + $type = trim(strtolower($type)); + + // Registrar el tipo de contacto después de la normalización + $this->logger->info("Tipo de contacto normalizado: '$type'"); + + // Asegurarse de que $phones es un array y recorrerlo + if (is_array($phones)) { + foreach ($phones as $phone) { + switch ($type) { + case 'whatsapp': + // Ejec vb bbnbbnbvnutar función de notificación y actualización + $this->onlyUpdate($notificationData, $phone, false); + break; + + case 'whatsactualiza': + // Ejecutar función de actualización + $this->onlyUpdate($notificationData, $phone, false); + break; + + default: + // Registrar cuando el tipo no es reconocido + $this->logger->info("Tipo de contacto no reconocido: '$type'" . PHP_EOL); + break; + } + } + } else { + $this->logger->info("Formato no esperado para los números de teléfono en el tipo: $type" . PHP_EOL); + } + } + } + + /* + * Notify to client with the payment receipt via WhatsApp + */ + public function notify(NotificationData $notificationData, $phoneToNotify = null): void + { + + // $notification_client_data = $notificationData->clientData; //array con los datos del cliente + + // $notification_client_data_export = json_encode($notification_client_data); + // $this->logger->info("Valor de notification client data export: " . $notification_client_data_export . PHP_EOL); + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + // the "exportFormat" key must be defined in plugin's manifest file, see the link above + $IPServer = $config['ipserver']; + $UCRMAPIToken = $config['apitoken']; + $CallBellAPIToken = $config['tokencallbell']; + $notificationTypeText = $config['notificationTypeText']; + + $client_callbell_api = new ClientCallBellAPI($UCRMAPIToken, $IPServer, $CallBellAPIToken); + + //$clientPhoneNumber = $this->clientPhoneNumber->getUcrmClientNumber($notificationData); + $clientPhoneNumber = $this->validarNumeroTelefono($phoneToNotify); //Obtiene el número de celular del cliente que sea del tipo de contacto "WhatsApp" + + //$this->logger->debug("Numero de cel obtenido " . $clientPhoneNumber . PHP_EOL); + + if (empty($clientPhoneNumber)) { + $this->logger->warning('No se encontró un teléfono celular válido para el cliente: ' . $notificationData->clientId); + return; + } else { + + if ($notificationTypeText) { + try { + $attempts = 0; + $maxAttempts = 3; + $result = false; + + while ($attempts < $maxAttempts && !$result) { + $attempts++; + $result = $client_callbell_api->sendTextPaymentNotificationWhatsApp($clientPhoneNumber, $notificationData); + + if (!$result) { + $this->logger->warning("Intento $attempts fallido para enviar notificación de texto al cliente con número $clientPhoneNumber." . PHP_EOL); + } + } + + if ($result) { + $this->logger->info("Notificación de texto enviada correctamente al cliente con número $clientPhoneNumber después de $attempts intento(s)." . PHP_EOL); + } else { + $this->logger->error("No se pudo enviar la notificación de texto al cliente con número $clientPhoneNumber después de $maxAttempts intentos." . PHP_EOL); + } + } catch (HttpException $httpException) { + $this->logger->error($httpException->getCode() . ' ' . $httpException->getMessage()); + } + + } else { + try { + $attempts = 0; + $maxAttempts = 3; + $result = false; + + while ($attempts < $maxAttempts && !$result) { + $attempts++; + $result = $client_callbell_api->sendPaymentNotificationWhatsApp($clientPhoneNumber, $notificationData); + + if (!$result) { + $this->logger->warning("Intento $attempts fallido para enviar notificación al cliente con número $clientPhoneNumber." . PHP_EOL); + } + } + + if ($result) { + $this->logger->info("Notificación enviada correctamente al cliente con número $clientPhoneNumber después de $attempts intento(s)." . PHP_EOL); + } else { + $this->logger->error("No se pudo enviar la notificación al cliente con número $clientPhoneNumber después de $maxAttempts intentos." . PHP_EOL); + } + } catch (HttpException $httpException) { + $this->logger->error($httpException->getCode() . ' ' . $httpException->getMessage()); + } + + } + + } + + // $messageBody = $this->messageTextFactory->createBody($notificationData); + // if (! $messageBody) { + // $this->logger->info('No text configured for event: ' . $notificationData->eventName); + // return; + // } + + } + + /* + * Notify to client with the payment receipt via WhatsApp and Update the client's data at CallBell + */ + public function notifyAndUpdate(NotificationData $notificationData, $phoneToNotifyAndUpdate = null): void + { + $this->logger->debug("***Se notifica y actualiza al cliente sobre su pago***" . PHP_EOL); + + // $notification_client_data = $notificationData->clientData; //array con los datos del cliente + + // $notification_client_data_export = json_encode($notification_client_data); + // $this->logger->info("Valor de notification client data export: " . $notification_client_data_export . PHP_EOL); + //$this->logger->debug("Creando instancia callbell ".PHP_EOL); + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + // the "exportFormat" key must be defined in plugin's manifest file, see the link above + $IPServer = $config['ipserver']; + $UCRMAPIToken = $config['apitoken']; + $CallBellAPIToken = $config['tokencallbell']; + $notificationTypeText = $config['notificationTypeText']; + + $client_callbell_api = new ClientCallBellAPI($UCRMAPIToken, $IPServer, $CallBellAPIToken); + //$this->logger->debug(" instancia callbell creada".PHP_EOL); + //$clientPhoneNumber = $this->validarNumeroTelefono($this->clientPhoneNumber->getUcrmClientNumber($notificationData)); + + //$clientPhoneNumber = $this->clientPhoneNumber->getUcrmClientNumber($notificationData); + //$clientPhoneNumber = $this->validarNumeroTelefono($this->clientPhoneNumber->getUcrmClientNumber($notificationData)); //Obtiene el número de celular del cliente que sea del tipo de contacto "WhatsApp" + + $clientPhoneNumber = $this->validarNumeroTelefono($phoneToNotifyAndUpdate); + + if (empty($clientPhoneNumber)) { + $this->logger->warning('No se encontró un teléfono celular válido para el cliente: ' . $notificationData->clientId); + return; + } else { + + //$this->logger->debug(sprintf('llego al llamado de sendPaymentNotificationWhatsApp con client_id: %s y número de celular: %s', $notificationData->clientId, $clientPhoneNumber)); + + if ($notificationTypeText) { + $this->logger->debug("Activado el check de mensajes de texto: " . $notificationTypeText . PHP_EOL); + } + + if ($notificationTypeText) { + try { + + if ($client_callbell_api->sendTextPaymentNotificationWhatsApp($clientPhoneNumber, $notificationData)) { + $response_getContactCallBell = json_decode($client_callbell_api->getContactWhatsapp($clientPhoneNumber), true); + $client_callbell_api->patchWhatsapp($response_getContactCallBell, $notificationData); + } else { + $this->logger->warning("No se pudo enviar el comprobante para este cliente" . PHP_EOL); + } + } catch (\Exception $Exception) { + $this->logger->error($Exception->getCode() . ' ' . $Exception->getMessage()); + } + + } else { + + try { + if ($client_callbell_api->sendPaymentNotificationWhatsApp($clientPhoneNumber, $notificationData)) { + $response_getContactCallBell = json_decode($client_callbell_api->getContactWhatsapp($clientPhoneNumber), true); + $client_callbell_api->patchWhatsapp($response_getContactCallBell, $notificationData); + } else { + $this->logger->warning("No se pudo enviar el comprobante para este cliente" . PHP_EOL); + } + + } catch (\Exception $Exception) { + $this->logger->error($Exception->getCode() . ' ' . $Exception->getMessage()); + } + } + + } + + // $messageBody = $this->messageTextFactory->createBody($notificationData); + // if (! $messageBody) { + // $this->logger->info('No text configured for event: ' . $notificationData->eventName); + // return; + // } + + } + + /* + * Notify to client about Invoice Overdue + */ + public function notifyOverDue(NotificationData $notificationData): void + { + $this->logger->debug("***Se notifica al cliente que la factura de su servicio está vencida***" . PHP_EOL); + + // $notification_client_data = $notificationData->clientData; //array con los datos del cliente + + // $notification_client_data_export = json_encode($notification_client_data); + // $this->logger->info("Valor de notification client data export: " . $notification_client_data_export . PHP_EOL); + //$this->logger->debug("Creando instancia callbell ".PHP_EOL); + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + // the "exportFormat" key must be defined in plugin's manifest file, see the link above + $IPServer = $config['ipserver']; + $UCRMAPIToken = $config['apitoken']; + $CallBellAPIToken = $config['tokencallbell']; + + $client_callbell_api = new ClientCallBellAPI($UCRMAPIToken, $IPServer, $CallBellAPIToken); + //$this->logger->debug(" instancia callbell creada".PHP_EOL); + //$clientPhoneNumber = $this->validarNumeroTelefono($this->clientPhoneNumber->getUcrmClientNumber($notificationData)); + $clientPhoneNumber = $this->clientPhoneNumber->getUcrmClientNumber($notificationData); + $this->logger->debug("Numero de cel obtenido " . $clientPhoneNumber . PHP_EOL); + + if (empty($clientPhoneNumber)) { + $this->logger->warning('No se encontró un teléfono celular válido para el cliente: ' . $notificationData->clientId); + return; + } else { + + try { + //$this->logger->debug(sprintf('llego al llamado de sendPaymentNotificationWhatsApp con client_id: %s y número de celular: %s', $notificationData->clientId, $clientPhoneNumber)); + //$this->sendMessage($notificationData, $clientSmsNumber, $messageBody); + //$this->sendWhatsApp($notificationData, $clientSmsNumber); + + $client_callbell_api->sendOverdueNotificationWhatsApp($clientPhoneNumber, $notificationData); + + } catch (HttpException $httpException) { + //$this->logger->debug('Ocurrio un error en el try catch'); + $this->logger->error($httpException->getCode() . ' ' . $httpException->getMessage()); + } + } + + // $messageBody = $this->messageTextFactory->createBody($notificationData); + // if (! $messageBody) { + // $this->logger->info('No text configured for event: ' . $notificationData->eventName); + // return; + // } + + } + + /* + * Update the client's data at CallBell + */ + public function onlyUpdate(NotificationData $notificationData, $phoneToUpdate = null, $updateEmail = false): void + { + //$this->logger->info("Se enviará una actualización a Callbell " . PHP_EOL); + + $notification_client_data = $notificationData->clientData; //array con los datos del cliente + + // $notification_client_data_export = json_encode($notification_client_data); + // $this->logger->info("Valor de notification client data export: " . $notification_client_data_export . PHP_EOL); + + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + // the "exportFormat" key must be defined in plugin's manifest file, see the link above + $IPServer = $config['ipserver']; + $UCRMAPIToken = $config['apitoken']; + $CallBellAPIToken = $config['tokencallbell']; + $StripeToken = $config['tokenstripe']; + + $attributes = $notificationData->clientData['attributes']; //Obtener los atributos del cliente + // Variable para almacenar los valores de los atributos que comienzan con "clabe" + + // Iterar sobre los atributoss + foreach ($attributes as $attribute) { + // Verificar si la "key" comienza con "stripe" + if (strpos($attribute['key'], 'stripe') === 0) { + // Agregar el valor al array $clabeValues + $customerStripeID = $attribute['value']; + } else if (strpos($attribute['key'], 'clabe') === 0) { // Verificar si la "key" comienza con "clabe" + // Agregar el valor al array $clabeValues + $clabeInterbancaria = $attribute['value']; + } + + } + + if ($updateEmail) { + $contacts = $notificationData->clientData['contacts'] ?? []; //Obtener los contactos que tiene el cliente para buscar el email + + foreach ($contacts as $contact) { + + $types = $contact['types'] ?? []; //Obtener los tipos de contactos del cliente + foreach ($types as $type) { + + if ($type['name'] == 'WhatsApp') { //Si el tipo de contacto es Whatsapp + + $client_email = $contact['email']; //Asignar el correo del cliente a la variable + break; + } + } + + } + $this->logger->info("Se procesaron los contactos para revisar el o los email" . PHP_EOL); + + $stripe = new \Stripe\StripeClient($StripeToken); //Instancia de la clase manejadora de clientes para la API de Stripe + + if ($client_email !== null && $customerStripeID !== null) { + $this->logger->info("Se actualizara el correo del cliente en Stripe: " . $client_email . PHP_EOL); + $stripe->customers->update($customerStripeID, ['email' => $client_email]); //Actualiza el correo electrónico del cliente en la plataforma de Stripe en su correspondiente "customer Stripe ID" + } + } + + $clientPhoneNumber = $this->validarNumeroTelefono($phoneToUpdate); //Obtiene el número de celular del cliente que sea del tipo de contacto "WhatsApp" + + if (empty($clientPhoneNumber)) { + $this->logger->warning('No se encontró un teléfono celular válido para el cliente: ' . $notificationData->clientId); + return; + } else { + + try { + //$this->logger->debug(sprintf('llego al llamado de sendwhatsapp con client_id: %s y número de celular: %s', $notificationData->clientId, $clientPhoneNumber)); + //$this->sendMessage($notificationData, $clientSmsNumber, $messageBody); + //$this->sendWhatsApp($notificationData, $clientSmsNumber); + + //$client_callbell_api->sendMessageWhatsApp($clientPhoneNumber, $notificationData); + $client_callbell_api = new ClientCallBellAPI($UCRMAPIToken, $IPServer, $CallBellAPIToken); + $response_getContactCallBell = json_decode($client_callbell_api->getContactWhatsapp($clientPhoneNumber), true); + //$this->logger->debug('Se hizo la petición al callbell para obtener el uuid' . PHP_EOL); + $client_callbell_api->patchWhatsapp($response_getContactCallBell, $notificationData, $clabeInterbancaria); + + } catch (HttpException $httpException) { + //$this->logger->debug('Ocurrio un error en el try catch'); + $this->logger->error($httpException->getCode() . ' ' . $httpException->getMessage()); + } + } + + // $notificationData_export = var_export($notificationData, true); + // $this->logger->debug('valor de notificationdata: ' . $notificationData_export); + + // $messageBody = $this->messageTextFactory->createBody($notificationData); + // if (! $messageBody) { + // $this->logger->info('No text configured for event: ' . $notificationData->eventName); + // return; + // } + + } + + /* + * Update de status of service at CallBell + */ + public function onlyUpdateService(NotificationData $notificationData, $phoneToUpdate): void + { + //$this->logger->info("Se enviará una actualización del estado del servicio a Callbell, estado del servicio: ".$notificationData->serviceData['status'] . PHP_EOL); + $this->logger->info(json_encode($notificationData) . PHP_EOL); + + //$clientID = $notificationData->extraData['entity']['clientId']; + + //$this->logger->info("client id " .$clientID. PHP_EOL); + + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + // the "exportFormat" key must be defined in plugin's manifest file, see the link above + $IPServer = $config['ipserver']; + $UCRMAPIToken = $config['apitoken']; + $CallBellAPIToken = $config['tokencallbell']; + + $clientPhoneNumber = $this->validarNumeroTelefono($phoneToUpdate); //Obtiene el número de celular del cliente que sea del tipo de contacto "WhatsApp" + //$this->logger->info("Número de telefono obtenido para actualizar:" .$clientPhoneNumber. PHP_EOL); + + if (empty($clientPhoneNumber)) { + $this->logger->warning('No se encontró un teléfono celular válido para el cliente: ' . $notificationData->clientId); + return; + } else { + + try { + //$this->logger->debug(sprintf('llego al llamado de sendwhatsapp con client_id: %s y número de celular: %s', $notificationData->clientId, $clientPhoneNumber)); + //$this->sendMessage($notificationData, $clientSmsNumber, $messageBody); + //$this->sendWhatsApp($notificationData, $clientSmsNumber); + + $client_callbell_api = new ClientCallBellAPI($UCRMAPIToken, $IPServer, $CallBellAPIToken); + $response_getContactCallBell = json_decode($client_callbell_api->getContactWhatsapp($clientPhoneNumber), true); + $client_callbell_api->patchServiceStatusWhatsApp($response_getContactCallBell, $notificationData); + + } catch (HttpException $httpException) { + //$this->logger->debug('Ocurrio un error en el try catch'); + $this->logger->error($httpException->getCode() . ' ' . $httpException->getMessage()); + } + } + + } + + /** + * implement in subclass with the specific messaging provider + * @see TwilioNotifierFacade::sendMessage() + */ + abstract protected function sendMessage( + NotificationData $notificationData, + string $clientSmsNumber, + string $messageBody + ): void; + + // /** + // * implement in subclass with the specific messaging provider + // * @see TwilioNotifierFacade::sendWhatsApp() + // */ + // abstract protected function sendWhatsApp( + // NotificationData $notificationData, + // string $clientSmsNumber + // ): void; + + function validarNumeroTelefono($telefono) + { + // Eliminar espacios y guiones + $telefono = preg_replace('/\s+|-/', '', $telefono); + + // Eliminar caracteres no numéricos, excepto el '+' si está al principio + $telefono = preg_replace('/(?!^\+)[^0-9]/', '', $telefono); + + // Verificar si el número comienza con "+1" o "1" y tiene 11 dígitos en total + if ( + (substr($telefono, 0, 2) === "+1" && strlen($telefono) === 12) || + (substr($telefono, 0, 1) === "1" && strlen($telefono) === 11) + ) { + return $telefono; + } + + // Si el número tiene exactamente 10 dígitos, agregar el prefijo "521" + if (strlen($telefono) === 10) { + return "521" . $telefono; + } + + // Si el número tiene más de 10 dígitos, verificar que comience con "521" + if (strlen($telefono) > 10) { + if (substr($telefono, 0, 2) === "521" && strlen($telefono) === 12) { + return $telefono; + } else { + // Si no comienza con "52", tomar los últimos 10 dígitos y agregar el prefijo "52" + return "521" . substr($telefono, -10); + } + } + + // Si no cumple con ninguna de las condiciones anteriores, retornar cadena vacía + return ''; + } + + function patchClientCustomAttribute($clientId, $attributeId, $value): bool + { + $logger = \Ubnt\UcrmPluginSdk\Service\PluginLogManager::create(); + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + $ucrmBaseUri = $config['ipserver']; + $authToken = $config['apitoken']; + + $ucrmBaseUri = 'https://' . $ucrmBaseUri . '/crm/api/v1.0/'; + $clientguzz = new Client(); //instancia de cliente GuzzleHttp para consumir API UISP CRM + + $json_data_patch = '{ + "attributes": [ + { + "value": "' . $value . '", + "customAttributeId":' . $attributeId . ' + } + ] + + }'; //JSON para hacer patch de los custom fields del cliente en el UISCP CRM + + try { + //aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM + $responseCRM = $clientguzz->patch( + $ucrmBaseUri . 'clients/' . $clientId, + [ + 'headers' => [ + 'X-Auth-Token' => $authToken, + 'Content-Type' => 'application/json', + ], + 'body' => $json_data_patch, + 'verify' => false, // Deshabilitar la verificación del certificado SSL + ], + + ); + //evaluar el código de respuesta HTTP y si es 200 se retorna true + if ($responseCRM->getStatusCode() === 200) { + $this->logger->info("Se actualizó el custom attribute del cliente " . $clientId . PHP_EOL); + return true; // Return true if patch is successful + } else { + $this->logger->info("Error al hacer el patch al CRM: " . $responseCRM->getStatusCode() . PHP_EOL); + return false; // Return false if patch fails + } + + + } catch (\GuzzleHttp\Exception\GuzzleException $error) { + $this->logger->info("Error al hacer el patch al CRM: " . $error->getMessage() . PHP_EOL); + return false; // Return false if patch fails + } + + } + function getVaultCredentials($dataToSearch): string + { + + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + $ucrmBaseUri = $config['ipserver']; + $crmAuthToken = $config['apitoken']; + $unmsAuthToken = $config['unmsApiToken']; + + if ($ucrmBaseUri === '172.16.5.134') { //opción solo para el servidor de pruebas + return 'gYAIEK:Be}SK*01z5+/V'; + } + + $unmsBaseUri = 'https://' . $ucrmBaseUri . '/nms/api/v2.1/'; + $ucrmBaseUri = 'https://' . $ucrmBaseUri . '/crm/api/v1.0/'; + //$authToken = '7adc9198-50b1-41d0-9bfa-d4946902ed89'; + + // Crear una instancia del cliente Guzzle + $clientUnms = new Client([ + 'base_uri' => $unmsBaseUri, + 'verify' => false, // Deshabilitar la verificación del certificado SSL + ]); + + $clientUcrm = new Client([ + 'base_uri' => $ucrmBaseUri, + 'verify' => false, // Deshabilitar la verificación del certificado SSL + ]); + + //validar si dataToSearch es una dirección IP o una dirección MAC + if (filter_var($dataToSearch, FILTER_VALIDATE_IP)) { + + // La variable es una dirección IP válida + //$logger->appendLog('Consulta por dirección IP: ' . $dataToSearch); + $this->logger->info('Consulta por dirección IP: ' . $dataToSearch . PHP_EOL); + + + if ($ucrmBaseUri === '172.16.5.134') { //opción solo para el servidor de pruebas + return 'gYAIEK:Be}SK*01z5+/V'; + } + + $IpAddressClientId = filter_var($dataToSearch, FILTER_VALIDATE_IP); + + try { + $responseSitesByIP = $clientUnms->request('GET', 'sites/search?query=' . $dataToSearch . '&count=1&page=1', [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + + if ($responseSitesByIP->getStatusCode() === 200) { + + $datasSitesByIP = json_decode((string) $responseSitesByIP->getBody(), true); + $jsonDevicesBySite = json_encode($datasSitesByIP, JSON_PRETTY_PRINT); + //print_r($jsonDevicesBySite . PHP_EOL); //Devices por ID del sitio + + if (isset($datasSitesByIP[0])) { + $siteId = $datasSitesByIP[0]['id']; + // print_r('ID DEL SITIO: ' . $siteId . PHP_EOL); // ID del sitio + } else { + + $this->logger->error('No se encontró ningún sitio para la IP proporcionada: ' . $dataToSearch . PHP_EOL); + return 'Error: No se encontró ningún sitio para la IP proporcionada: ' . $dataToSearch; // Return early if no site is found + } + + } else { + + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responseSitesByIP->getStatusCode() . PHP_EOL); + return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responseSitesByIP->getStatusCode(); // Return early if the request fails + } + + $devicesBySiteId = $clientUnms->request('GET', 'devices?siteId=' . $siteId, [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + + if ($devicesBySiteId->getStatusCode() === 200) { + $dataDevicesBySite = json_decode((string) $devicesBySiteId->getBody(), true); + $jsonDevicesBySite = json_encode($dataDevicesBySite, JSON_PRETTY_PRINT); + //print_r($jsonDevicesBySite . PHP_EOL); //Devices por ID del sitio + $deviceID = null; + + foreach ($dataDevicesBySite as $device) { + if (isset($device['ipAddress'])) { + $ipAddress = explode('/', $device['ipAddress'])[0]; // Obtener solo la IP sin la máscara + if ($ipAddress === $IpAddressClientId) { + $deviceID = $device['identification']['id']; + break; // Salir del ciclo si se encuentra la IP + } + } else { + $this->logger->error('No se encontró la IP del dispositivo en la respuesta de la API.' . PHP_EOL); + return 'Error: No se encontró la IP del dispositivo en la respuesta de la API.'; // Return early if the IP is not found + } + } + + if ($deviceID == null) { + $this->logger->error('No se encontró el dispositivo con la IP proporcionada: ' . $IpAddressClientId . PHP_EOL); + return 'Error: No se encontró el dispositivo con la IP proporcionada: ' . $IpAddressClientId; // Return early if no device is found + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $devicesBySiteId->getStatusCode() . PHP_EOL); + return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $devicesBySiteId->getStatusCode(); // Return early if the request fails + } + + $responsePasswordVault = $clientUnms->request('GET', 'vault/' . $deviceID . '/credentials', [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + + if ($responsePasswordVault->getStatusCode() === 200) { + $dataPasswordVault = json_decode((string) $responsePasswordVault->getBody(), true); + // $jsonPasswordVault = json_encode($dataPasswordVault, JSON_PRETTY_PRINT); + //print_r($jsonPasswordVault . PHP_EOL); //Credenciales del dispositivo + + if (isset($dataPasswordVault['credentials'][0]['password'])) { + $dataPasswordVault = $dataPasswordVault['credentials'][0]['password']; + } else { + $this->logger->error('No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida.' . PHP_EOL); + return "Error: No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida."; // Return early if the password is not found + } + return $dataPasswordVault; + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode() . PHP_EOL); + return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode(); // Return early if the request fails + } + + } catch (RequestException $requestException) { + // Manejar errores de la solicitud, si hay error 404 entonces responder que no se encontró niguna IP asociada a alguna antena o dispositivo de red + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + + if ($statusCode == 404) { + $this->logger->error('No se encontró el cliente con la dirección IP proporcionada: ' . $IpAddressClientId . PHP_EOL); + return 'Error: No se encontró el cliente con la dirección IP proporcionada: ' . $IpAddressClientId; // Return early if the client is not found + } + return 'Error: ' . $reason; // Return early if the request fails + } else { + $this->logger->error('Error al realizar la solicitud: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + + } + + } else if (preg_match('/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/', $dataToSearch)) { + // La variable es una dirección MAC válida + + $this->logger->info('Consulta por dirección MAC: ' . $dataToSearch . PHP_EOL); + + if ($ucrmBaseUri === '172.16.5.134') { //opción solo para el servidor de pruebas + return 'gYAIEK:Be}SK*01z5+/V'; + } + + try { + // //para mandarla al endpoint de dispositivos por MAC se necesita convertir la cadena de la direccion mac en un formato como el siguiente ejemplo: para la dirección mac 60:22:32:c8:b2:c3 quedaría como 60%3A22%3A32%3Ac8%3Ab2%3Ac3 + // $dataToSearch = str_replace(':', '%3A', $dataToSearch); + // $logger->appendLog('Consulta por dirección MAC: ' . $dataToSearch ); + $responseDeviceByMAC = $clientUnms->request('GET', 'devices/mac/' . $dataToSearch, [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + + if ($responseDeviceByMAC->getStatusCode() === 200) { + + $dataDeviceByMAC = json_decode((string) $responseDeviceByMAC->getBody(), true); + $jsonDeviceByMac = json_encode($dataDeviceByMAC, JSON_PRETTY_PRINT); + // print_r($jsonDeviceByMac . PHP_EOL); //Devices por ID del sitio + //$logger->appendLog($jsonDeviceByMac.PHP_EOL); + + //Ejemplo de $responseDeviceByMac en json: {"id":"84c8a581-154c-41db-81d5-a1b1c9ede411","type":"airMax","series":"AC"} + //obtener el id del dispositivo y si no existe el id del dispositivo entonces devolver un mensaje de error + if (isset($dataDeviceByMAC['id'])) { + $deviceId = $dataDeviceByMAC['id']; + // print_r('ID DEL DISPOSITIVO: ' . $deviceId . PHP_EOL); // ID del dispositivo + } else { + $this->logger->error('No se encontró el dispositivo con la dirección MAC proporcionada: ' . $dataToSearch . PHP_EOL); + return 'Error: No se encontró el dispositivo con la dirección MAC proporcionada: ' . $dataToSearch; // Return early if no device is found + } + + } + + $responsePasswordVault = $clientUnms->request('GET', 'vault/' . $deviceId . '/credentials', [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + + if ($responsePasswordVault->getStatusCode() === 200) { + $dataPasswordVault = json_decode((string) $responsePasswordVault->getBody(), true); + + if (isset($dataPasswordVault['credentials'][0]['password'])) { + $dataPasswordVault = $dataPasswordVault['credentials'][0]['password']; + } else { + $this->logger->error('No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida.' . PHP_EOL); + return "Error: No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida."; // Return early if the password is not found + } + return $dataPasswordVault; + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode() . PHP_EOL); + return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode(); // Return early if the request fails + } + + } catch (RequestException $requestException) { + // Manejar errores de la solicitud, si hay error 404 entonces responder que no se encontró niguna MAC asociada a alguna antena o dispositivo de red + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + + if ($statusCode == 404) { + $this->logger->error('No se encontró ninguna antena de cliente o dispositivo en la red con la dirección MAC proporcionada: ' . $dataToSearch . PHP_EOL); + return 'Error: No se encontró ninguna antena de cliente o dispositivo en la red con la dirección MAC proporcionada: ' . $dataToSearch; // Return early if the client is not found + } + $this->logger->error('Error: ' . $reason . PHP_EOL); + return 'Error: ' . $reason; // Return early if the request fails + } else { + $this->logger->error('Error al realizar la solicitud: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + + } + + } else { + // La variable no es una dirección IP válida, se asume que es un ID + $IpAddressClientId = filter_var($dataToSearch, FILTER_SANITIZE_NUMBER_INT); + //print ('Consulta por ID: ' . $dataToSearch . PHP_EOL); + $this->logger->info('Consulta por ID: ' . $dataToSearch . PHP_EOL); + + + try { + //Obtener id del sitio por medio del servicio + $responseServices = $clientUcrm->get('clients/services?clientId=' . $IpAddressClientId, [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + 'Content-Type: application/json', + ], + ]); + + } catch (RequestException $requestException) { + // Manejar errores de la solicitud + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + //si el statusCode es 404 significa que no se encontró el cliente + if ($statusCode == 404) { + $this->logger->error('No se encontró el cliente con el ID proporcionado: ' . $IpAddressClientId . PHP_EOL); + return 'Error: No se encontró el cliente con el ID proporcionado: ' . $IpAddressClientId; // Return early if the client is not found + } + return 'Error: ' . $reason; // Return early if the request fails + + } else { + $this->logger->error('Error: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + + } + + if ($responseServices->getStatusCode() === 200) { + + $dataServices = json_decode($responseServices->getBody()->getContents(), true); + + if (isset($dataServices[0])) { + $unmsSiteID = $dataServices[0]['unmsClientSiteId']; // Example: 9c6798f3-0254-4e5b-bc3b-9da82fe16e46 + } else { + $this->logger->error('No se encontraron servicios para el cliente proporcionado: ' . $IpAddressClientId . PHP_EOL); + return "Error: No se encontraron servicios para el cliente proporcionado: " . $IpAddressClientId; // Return early if no services are found + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responseServices->getStatusCode() . PHP_EOL); + return "Error: En la solicitud. Código de estado HTTP: " . $responseServices->getStatusCode(); + } + + try { + $responseDevicesBySite = $clientUnms->request('GET', 'devices?siteId=' . $unmsSiteID, [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + } catch (RequestException $requestException) { + // Manejar errores de la solicitud + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + //si el statusCode es 404 significa que no se encontró el cliente + if ($statusCode == 404) { + $this->logger->error('No se encontró el devicie con el ID proporcionado: ' . $IpAddressClientId . PHP_EOL); + return 'Error: No se encontró el devicie con el ID proporcionado: ' . $IpAddressClientId; // Return early if the client is not found + } + $this->logger->error('Error: ' . $reason . PHP_EOL); + return 'Error: ' . $reason; // Return early if the request fails + + } else { + $this->logger->error('Error: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + + } + + if ($responseDevicesBySite->getStatusCode() === 200) { + + $dataDevicesBySite = json_decode($responseDevicesBySite->getBody()->getContents(), true); + $jsonDevicesBySite = json_encode($dataDevicesBySite, JSON_PRETTY_PRINT); + //print_r($jsonDevicesBySite . PHP_EOL); //Devices por ID del sitio + + //id del device al que está conectado el cliente + if (isset($dataDevicesBySite[0])) { + //verificar con iiset si existe la clave 'identification' y 'apDevice' en el primer elemento del array + if (isset($dataDevicesBySite[0]['identification']) && isset($dataDevicesBySite[0]['attributes']['apDevice'])) { + $idClientDevice = $dataDevicesBySite[0]['identification']['id']; + $deviceID = $dataDevicesBySite[0]['attributes']['apDevice']['id']; + } else { + $this->logger->error('No se encontró el dispositivo con el ID proporcionado: ' . $IpAddressClientId . PHP_EOL); + return "Este cliente es un repetidor."; // Return early if the key is not found + } + + } else { + $this->logger->error('No se encontraron dispositivos para el sitio proporcionado: ' . $unmsSiteID . PHP_EOL); + return "Error: No se encontraron dispositivos para el sitio proporcionado."; // Return early if no devices are found + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode() . PHP_EOL); + return "Error: Falla en la solicitud. Código de estado HTTP: " . $responseDevicesBySite->getStatusCode(); + } + + try { + $responseDevicesBySite = $clientUnms->request('GET', 'devices/' . $deviceID, [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + } catch (RequestException $requestException) { + // Manejar errores de la solicitud + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + //si el statusCode es 404 significa que no se encontró el cliente + if ($statusCode == 404) { + $this->logger->error('No se encontró el device con el ID proporcionado: ' . $IpAddressClientId . PHP_EOL); + return 'Error: No se encontró el device con el ID proporcionado: ' . $IpAddressClientId; // Return early if the client is not found + } + return 'Error: ' . $reason; // Return early if the request fails + + } else { + $this->logger->error('Error: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + + } + + if ($responseDevicesBySite->getStatusCode() === 200) { + + $dataDevices = json_decode($responseDevicesBySite->getBody()->getContents(), true); + $jsonDevices = json_encode($dataDevices, JSON_PRETTY_PRINT); + //print_r($jsonDevices . PHP_EOL); + + try { + //print_r('ID del device al que está conectado el cliente: ' . $idDevice . PHP_EOL); + $responsePasswordVault = $clientUnms->request('GET', 'vault/' . $idClientDevice . '/credentials', [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + } catch (RequestException $requestException) { + // Manejar errores de la solicitud + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + //si el statusCode es 404 significa que no se encontró el cliente + if ($statusCode == 404) { + $this->logger->error('No se encontró el device con el ID proporcionado: ' . $IpAddressClientId . PHP_EOL); + return 'Error: No se encontró el device con el ID proporcionado: ' . $IpAddressClientId; // Return early if the client is not found + } + $this->logger->error('Error: ' . $reason . PHP_EOL); + return 'Error: ' . $reason; // Return early if the request fails + + } else { + $this->logger->error('Error: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + } + + if ($responsePasswordVault->getStatusCode() === 200) { + $dataPasswordVault = json_decode($responsePasswordVault->getBody()->getContents(), true); + $jsonPasswordVault = json_encode($dataPasswordVault, JSON_PRETTY_PRINT); + if (isset($dataPasswordVault['credentials'][0]['password'])) { + $passwordVault = $dataPasswordVault['credentials'][0]['password']; + return $passwordVault; + } else { + $this->logger->error('No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida.' . PHP_EOL); + return "Error: No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida."; // Return early if the password is not found + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode() . PHP_EOL); + return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode(); // Return early if the request fails + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode() . PHP_EOL); + return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode(); // Return early if the request fails + + } + + } + + } + function getVaultCredentialsByClientId($clientId): string + { + + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + $ipServer = $config['ipserver']; + $crmAuthToken = $config['apitoken']; + $unmsAuthToken = $config['unmsApiToken']; + + if ($ipServer === '172.16.5.134') { //opción solo para el servidor de pruebas + return 'gYAIEK:Be}SK*01z5+/V'; + } + + $unmsBaseUri = 'https://' . $ipServer . '/nms/api/v2.1/'; + $ucrmBaseUri = 'https://' . $ipServer . '/crm/api/v1.0/'; + //$authToken = '7adc9198-50b1-41d0-9bfa-d4946902ed89'; + + // Crear una instancia del cliente Guzzle + $clientUnms = new Client([ + 'base_uri' => $unmsBaseUri, + 'verify' => false, // Deshabilitar la verificación del certificado SSL + ]); + + $clientUcrm = new Client([ + 'base_uri' => $ucrmBaseUri, + 'verify' => false, // Deshabilitar la verificación del certificado SSL + ]); + + + $this->logger->debug('Consulta por ID: ' . $clientId . PHP_EOL); + + + try { + //Obtener id del sitio por medio del servicio + $responseServices = $clientUcrm->get('clients/services?clientId=' . $clientId, [ + 'headers' => [ + 'X-Auth-Token' => $crmAuthToken, + 'Content-Type: application/json', + ], + ]); + + } catch (RequestException $requestException) { + // Manejar errores de la solicitud + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + //si el statusCode es 404 significa que no se encontró el cliente + if ($statusCode == 404) { + $this->logger->error('No se encontró el cliente con el ID proporcionado: ' . $clientId . PHP_EOL); + return 'No se encontró el cliente con el ID proporcionado: ' . $clientId; // Return early if the client is not found + } + return 'Error: ' . $reason; // Return early if the request fails + + } else { + $this->logger->error('Error: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + + } + + if ($responseServices->getStatusCode() === 200) { + + $dataServices = json_decode($responseServices->getBody()->getContents(), true); + + if (isset($dataServices[0])) { + $unmsSiteID = $dataServices[0]['unmsClientSiteId']; // Example: 9c6798f3-0254-4e5b-bc3b-9da82fe16e46 + } else { + $this->logger->error('No se encontraron servicios para el cliente proporcionado: ' . $clientId . PHP_EOL); + return "No se encontraron servicios para el cliente proporcionado: " . $clientId; // Return early if no services are found + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responseServices->getStatusCode() . PHP_EOL); + return "Error: En la solicitud. Código de estado HTTP: " . $responseServices->getStatusCode(); + } + + try { + $responseDevicesBySite = $clientUnms->request('GET', 'devices?siteId=' . $unmsSiteID, [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + } catch (RequestException $requestException) { + // Manejar errores de la solicitud + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + //si el statusCode es 404 significa que no se encontró el cliente + if ($statusCode == 404) { + $this->logger->error('No se encontró el device con el ID proporcionado: ' . $clientId . PHP_EOL); + return 'No se encontró el device con el ID proporcionado: ' . $clientId; // Return early if the client is not found + } + $this->logger->error('Error: ' . $reason . PHP_EOL); + return 'Error: ' . $reason; // Return early if the request fails + + } else { + $this->logger->error('Error: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + + } + + if ($responseDevicesBySite->getStatusCode() === 200) { + + $dataDevicesBySite = json_decode($responseDevicesBySite->getBody()->getContents(), true); + $jsonDevicesBySite = json_encode($dataDevicesBySite, JSON_PRETTY_PRINT); + //print_r($jsonDevicesBySite . PHP_EOL); //Devices por ID del sitio + + //id del device al que está conectado el cliente + if (isset($dataDevicesBySite[0])) { + //verificar con iiset si existe la clave 'identification' y 'apDevice' en el primer elemento del array + if (isset($dataDevicesBySite[0]['identification']) && isset($dataDevicesBySite[0]['attributes']['apDevice'])) { + $idClientDevice = $dataDevicesBySite[0]['identification']['id']; + $deviceID = $dataDevicesBySite[0]['attributes']['apDevice']['id']; + } else { + $this->logger->error('No se encontró el dispositivo con el ID proporcionado: ' . $clientId . PHP_EOL); + return "Este cliente es un repetidor."; // Return early if the key is not found + } + + } else { + $this->logger->error('No se encontraron dispositivos para el sitio proporcionado: ' . $unmsSiteID . PHP_EOL); + return "No se encontraron dispositivos para el sitio proporcionado."; // Return early if no devices are found + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode() . PHP_EOL); + return "Error: Falla en la solicitud. Código de estado HTTP: " . $responseDevicesBySite->getStatusCode(); + } + + try { + $responseDevicesBySite = $clientUnms->request('GET', 'devices/' . $deviceID, [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + } catch (RequestException $requestException) { + // Manejar errores de la solicitud + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + //si el statusCode es 404 significa que no se encontró el cliente + if ($statusCode == 404) { + $this->logger->error('No se encontró el device con el ID proporcionado: ' . $clientId . PHP_EOL); + return 'No se encontró el dispositivo de red con el ID proporcionado: ' . $clientId; // Return early if the client is not found + } + return 'Error: ' . $reason; // Return early if the request fails + + } else { + $this->logger->error('Error: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + + } + + if ($responseDevicesBySite->getStatusCode() === 200) { + + $dataDevices = json_decode($responseDevicesBySite->getBody()->getContents(), true); + $jsonDevices = json_encode($dataDevices, JSON_PRETTY_PRINT); + //print_r($jsonDevices . PHP_EOL); + + try { + //print_r('ID del device al que está conectado el cliente: ' . $idDevice . PHP_EOL); + $responsePasswordVault = $clientUnms->request('GET', 'vault/' . $idClientDevice . '/credentials', [ + 'headers' => [ + 'X-Auth-Token' => $unmsAuthToken, + ], + ]); + } catch (RequestException $requestException) { + // Manejar errores de la solicitud + if ($requestException->hasResponse()) { + $response = $requestException->getResponse(); + $statusCode = $response->getStatusCode(); + $reason = $response->getReasonPhrase(); + //si el statusCode es 404 significa que no se encontró el cliente + if ($statusCode == 404) { + $this->logger->error('No se encontró el device con el ID proporcionado: ' . $clientId . PHP_EOL); + return 'No se encontró el dispositivo de red con el ID proporcionado: ' . $clientId; // Return early if the client is not found + } + $this->logger->error('Error: ' . $reason . PHP_EOL); + return 'Error: ' . $reason; // Return early if the request fails + + } else { + $this->logger->error('Error: ' . $requestException->getMessage() . PHP_EOL); + return 'Error: ' . $requestException->getMessage(); // Return early if the request fails + } + } + + if ($responsePasswordVault->getStatusCode() === 200) { + $dataPasswordVault = json_decode($responsePasswordVault->getBody()->getContents(), true); + $jsonPasswordVault = json_encode($dataPasswordVault, JSON_PRETTY_PRINT); + if (isset($dataPasswordVault['credentials'][0]['password'])) { + $passwordVault = $dataPasswordVault['credentials'][0]['password']; + return $passwordVault; + } else { + $this->logger->error('No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida.' . PHP_EOL); + return "No se encontró una contraseña en la bóveda para la antena de este cliente, es altamente probable que conserve una contraseña conocida."; // Return early if the password is not found + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode() . PHP_EOL); + return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responsePasswordVault->getStatusCode(); // Return early if the request fails + } + + } else { + $this->logger->error('Falla en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode() . PHP_EOL); + return 'Error: Falla en la solicitud. Código de estado HTTP: ' . $responseDevicesBySite->getStatusCode(); // Return early if the request fails + + } + + + + } + + function comparePasswords($passwordVault, $passwordAntenaClienteCRM): string + { + $erroresVaultPrefix = [ + 'Error:', + 'No se encon', + 'Falla en', + ]; + + // Validar si la contraseña de la antena del cliente en el CRM es igual a la contraseña de la antena del cliente en el Vault y de ser iguales devolver la contraseña del vault + if ($passwordAntenaClienteCRM === $passwordVault) { + $this->logger->debug('La contraseña de la antena del cliente en el CRM + es igual a la contraseña de la antena del cliente en el Vault.' . PHP_EOL); + return $passwordVault; + } else { + //si no son iguales entonces validar si alguna de las contraseñas no contiene un prefijo de error para decidir cuál devolver; si + + + if (!empty($passwordAntenaClienteCRM) && !array_filter($erroresVaultPrefix, fn($prefix) => str_starts_with($passwordAntenaClienteCRM, $prefix))) { //verificar que la contraseña del CRM no comience con ninguno de los prefijos de error o esté vacía + $this->logger->debug('La contraseña de la antena del cliente en el CRM será la que se envíe al instalador.' . PHP_EOL); + return $passwordAntenaClienteCRM; + } elseif (!empty($passwordVault) && !array_filter($erroresVaultPrefix, fn($prefix) => str_starts_with($passwordVault, $prefix))) { //verificar que la contraseña del vault no comience con ninguno de los prefijos de error o esté vacía + $this->logger->debug('La contraseña de la antena del cliente en el Vault será la que se envíe al instalador.' . PHP_EOL); + return $passwordVault; + } else { + //asignar la variable con un texto de advertencia sobre que ninguna de las contraseñas es válida para ser enviada al instalador + $this->logger->debug('Ninguna de las contraseñas de la antena es válida para ser enviada al instalador.' . PHP_EOL); + return '⚠️ Probar una contraseña conocida como Siip.567 etc. ya que no se encontró una contraseña en el UISP Vault ni en el CRM.'; + } + } + } + +} diff --git a/archivos_webhook_destino/src/Facade/AbstractOxxoOperationsFacade.php b/archivos_webhook_destino/src/Facade/AbstractOxxoOperationsFacade.php new file mode 100755 index 0000000..0e237e2 --- /dev/null +++ b/archivos_webhook_destino/src/Facade/AbstractOxxoOperationsFacade.php @@ -0,0 +1,738 @@ +logger = $logger; + $this->messageTextFactory = $messageTextFactory; + $this->clientPhoneNumber = $clientPhoneNumber; + } + + /* + * Creates a PaymentIntent for OXXO in Stripe for a Customer + * @param array $event_json + * @param int|null $amount + * @return array + * @throws \GuzzleHttp\Exception\GuzzleException + * @throws \Stripe\Exception\ApiErrorException + * @throws Exception + */ + public function createOxxoPaymentIntent($event_json, $amount = null): array + { + //declarar un array asociativo de strings pero no asignarle nada aun + $arrayOxxoPayment = array(); + $integerAmount = $amount; + + $this->logger->info("Creando referencia del cliente para OXXO: " . PHP_EOL); + $configManager = PluginConfigManager::create(); + $config = $configManager->loadConfig(); + $StripeToken = $config['tokenstripe']; + $IPServer = $config['ipserver']; + $tokenCRM = $config['apitoken']; + $ipPuppeteer = $config['ipPuppeteer']; + $portPuppeteer = $config['portPuppeteer']; + + $baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; + $this->ucrmApi = UcrmApi::create(); + $currentUserAdmin = $this->ucrmApi->get('users/admins', []); + $clientID = $event_json['client_id']; + + $stripeCustomerId = null; + $clientEmail = ''; + $clientFullName = ''; + + $clientGuzzleHttp = new Client([ + 'base_uri' => $baseUri, + 'headers' => [ + 'X-Auth-App-Key' => $tokenCRM, + 'Accept' => 'application/json', + ], + 'verify' => false, + 'timeout' => 5, // Timeout de 5 segundos + ]); + + try { + $response = $clientGuzzleHttp->request('GET', "clients/" . $clientID); + $arrayClientCRM = json_decode($response->getBody()->getContents(), true); + //$this->logger->info("Valor de arrayClientCRM: " . print_r($arrayClientCRM, true) . PHP_EOL); + $clientFullName = $arrayClientCRM['firstName'] . ' ' . $arrayClientCRM['lastName']; + } catch (RequestException $e) { + if ($e->getCode() === 404) { + $this->logger->error("Cliente no encontrado en CRM: " . $clientID . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID, clientFullName y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorGetClientNotFound'; + $arrayOxxoPayment['failDescription'] = 'Cliente no encontrado en CRM: ' . $clientID; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $integerAmount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + + + } + //timeout + if ($e->getCode() === 408) { + $this->logger->error("Timeout al obtener el cliente en CRM: " . $clientID . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorTimeoutGetClient'; + $arrayOxxoPayment['failDescription'] = 'Timeout al obtener el cliente en CRM: ' . $clientID; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $integerAmount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + } + $this->logger->error("Error al obtener el cliente en CRM (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorGetClient'; + $arrayOxxoPayment['failDescription'] = 'Error al obtener el cliente en CRM: ' . $clientID; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $integerAmount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + + } + + // Obtener email del cliente + foreach ($arrayClientCRM['contacts'] as $contact) { + if (!empty($contact['email'])) { + $clientEmail = $contact['email']; + break; + } else { + $clientEmail = 'siip8873@gmail.com'; // Default + } + } + + try { + // Obtener stripeCustomerId + foreach ($arrayClientCRM['attributes'] as $attribute) { + if ($attribute['key'] === 'stripeCustomerId') { + $stripeCustomerId = $attribute['value']; + break; + } + } + + $this->logger->info("Stripe Customer ID obtenido: " . $stripeCustomerId . PHP_EOL); + } catch (Exception $e) { + $this->logger->error("Error al obtener el Customer ID de Stripe (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['error'] = 'errorGetCustomerStripe'; + $arrayOxxoPayment['failDescription'] = 'Error al obtener el Customer ID de Stripe: ' . $clientID; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $integerAmount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + } + + if ($amount === null) { + $amount = abs($arrayClientCRM['accountOutstanding']); //Monto adeudado del cliente + } else { + //la variable $amount debe ser numérica pero en ocasiones la solicitud puede traer signos o alguna letra ingresada mal por el cliente, se debe analizar para limpiar cualquier caracter que no sea numérico + if(!is_numeric($amount)) { + $amount = preg_replace('/[^\d.]/', '', $amount); // Eliminar todo lo que no sea dígito o punto decimal + } + $this->logger->debug("Monto proporcionado directamente: $amount " . PHP_EOL); + } + + if ($amount > 10) { + $amountInCents = intval($amount * 100); // Convertir a centavos + + try { + $this->logger->debug("Creando referencia en Stripe por $amount para el cliente $stripeCustomerId" . PHP_EOL); + $guzzleClient = new Client([ + 'timeout' => 5, // Timeout de 5 segundos + ]); + + $response = $guzzleClient->post('https://api.stripe.com/v1/payment_intents', [ + 'auth' => [$StripeToken, ''], + 'form_params' => [ + 'amount' => $amountInCents, + 'currency' => 'mxn', + 'payment_method_types' => ['customer_balance', 'card', 'oxxo'], + 'description' => 'Pago de servicio de SIIP Internet', + 'customer' => $stripeCustomerId, + 'metadata' => [ + 'clientId' => $clientID, + 'createdBy' => 'UCRM', + 'paymentType' => 'card.one_time', + 'signedInAdminId' => $currentUserAdmin[1]['id'], + 'tipoPago' => 'OXXO', + ], + 'payment_method_options' => [ + 'oxxo' => [ + 'expires_after_days' => 3, + ], + ], + ] + ]); + + $paymentIntent = json_decode($response->getBody()->getContents(), true); + + } catch (\Stripe\Exception\ApiConnectionException $e) { + $this->logger->error("Error de conexión con Stripe (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorConnectionStripe'; + $arrayOxxoPayment['failDescription'] = 'Error de conexión con Stripe: ' . $clientID; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $amount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + + } catch (\Stripe\Exception\ApiErrorException $e) { + $this->logger->error("Error de la API de Stripe: " . $e->getMessage() . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorApiStripe'; + $arrayOxxoPayment['failDescription'] = 'Error de la API de Stripe: ' . $clientID; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $amount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + + } catch (Exception $e) { + $this->logger->error("Error inesperado al crear PaymentIntent (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); + //si e->getMessage incluye el mensaje: You must provide a customer when creating or updating a PaymentIntent with a `customer_ba (truncated...) declarar una variable y ahi poner "Este cliente no tiene cuenta de Stripe" + if ($e->getMessage() == 'You must provide a customer when creating or updating a PaymentIntent with a `customer_ba (truncated...)') { + $this->logger->error("Este cliente no tiene cuenta de Stripe" . PHP_EOL); + $arrayOxxoPayment['failDescription'] = 'Este cliente no tiene cuenta de Stripe: ' . $clientID; + } else { + $this->logger->error("Error inesperado al crear PaymentIntent: " . $e->getMessage() . PHP_EOL); + $arrayOxxoPayment['failDescription'] = 'Error inesperado al crear PaymentIntent: ' . $clientID; + } + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorCreatePaymentIntent'; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $amount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + + } + + try { + $firstName = isset($arrayClientCRM['firstName']) ? trim($arrayClientCRM['firstName']) : ''; + $lastName = isset($arrayClientCRM['lastName']) ? trim($arrayClientCRM['lastName']) : ''; + + if (strlen($firstName) < 2 || strlen($lastName) < 2) { + $this->logger->error("Nombre/apellido inválido: ' . $firstName . ' ' . $lastName" . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorNombreApellidoInvalido'; + $arrayOxxoPayment['failDescription'] = "Nombre/apellido inválido: ' . $firstName . ' ' . $lastName"; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $amount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + + } + + $responsePaymentMethod = $guzzleClient->post('https://api.stripe.com/v1/payment_methods', [ + 'auth' => [$StripeToken, ''], + 'form_params' => [ + 'type' => 'oxxo', + 'billing_details' => [ + 'name' => "$firstName $lastName", + 'email' => $clientEmail, + ], + ] + ]); + + $paymentMethod = json_decode($responsePaymentMethod->getBody()->getContents(), true); + + $responseConfirmPaymentIntent = $guzzleClient->post('https://api.stripe.com/v1/payment_intents/' . $paymentIntent['id'] . '/confirm', [ + 'auth' => [$StripeToken, ''], + 'form_params' => [ + 'payment_method' => $paymentMethod['id'], + ] + ]); + + $paymentIntentConfirm = json_decode($responseConfirmPaymentIntent->getBody()->getContents(), true); + + } catch (Exception $e) { + $this->logger->error("Error al confirmar PaymentIntent: " . $e->getMessage() . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorConfirmPaymentIntent'; + $arrayOxxoPayment['failDescription'] = 'Error al confirmar PaymentIntent: ' . $e->getMessage(); + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $amount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + } + + if (!empty($paymentIntentConfirm['next_action']) && isset($paymentIntentConfirm['next_action']['oxxo_display_details'])) { + $oxxoPayment = $paymentIntentConfirm['next_action']['oxxo_display_details']; + $oxxo_reference = $oxxoPayment['number']; + $oxxo_receipt_url = $oxxoPayment['hosted_voucher_url']; + + $this->logger->info("Referencia OXXO: " . $oxxo_reference . PHP_EOL); + $this->logger->info("URL del recibo: " . $oxxo_receipt_url . PHP_EOL); + //$this->captureScreenshot($oxxo_receipt_url); + //devolver un array con los campos de url de oxxo, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = $oxxo_reference; + $arrayOxxoPayment['url'] = $oxxo_receipt_url; + $arrayOxxoPayment['error'] = ''; + $arrayOxxoPayment['failDescription'] = ''; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $amount; + $this->logger->info("Referencia OXXO creada correctamente." . PHP_EOL); + + + // Configuración de la API de tu servicio Docker de Puppeteer + $api_url = 'http://'.$ipPuppeteer.':'.$portPuppeteer.'/screenshot'; // Asegúrate que esta URL sea accesible + + // --- Datos para la petición --- + $request_data = [ + 'url' => $oxxo_receipt_url, // URL del recibo de OXXO + // Opcional: Si quieres recortar la imagen, descomenta y ajusta el 'clip' + + 'clip' => [ + 'x' => 325, + 'y' => 30, + 'width' => 550, + 'height' => 550 + ] + + ]; + + // Nombre del archivo donde se guardará la imagen + $clientFullNameWithoutSpaces = str_replace(' ', '_', $clientFullName); // Reemplazar espacios por guiones bajos + $voucherFileName = 'voucher_'.$clientFullNameWithoutSpaces.'_' . time() . '.jpeg'; // Usamos .jpeg por defecto + $output_filename = __DIR__ . '/../../vouchers_oxxo/' . $voucherFileName; // Usamos .jpeg por defecto + $output_filename = realpath(__DIR__ . '/../../vouchers_oxxo') . '/'. $voucherFileName; // Mejor: ruta absoluta y verifica que el directorio exista + //$output_filename = '/path/to/vouchers_oxxo/voucher_' . time() . '.jpeg'; // Mejor aún: ruta absoluta y fija, si es posible + + $guzzleClient = new Client([ + 'timeout' => 5, // Timeout de 5 segundos + ]); + // Descargar el recibo de OXXO + try { + // --- Realizar la petición POST --- + $response = $guzzleClient->post($api_url, [ + 'json' => $request_data, // Guzzle codifica automáticamente el array a JSON y establece Content-Type: application/json + 'headers' => [ + 'Accept' => 'image/jpeg, image/png', // Indicar que esperamos una imagen + ], + // Opcional: para depuración si necesitas ver el cuerpo de la respuesta en caso de error + // 'http_errors' => false // Desactiva las excepciones para códigos de estado 4xx/5xx y maneja la respuesta manualmente + ]); + + // --- Procesar la respuesta --- + $statusCode = $response->getStatusCode(); + $contentType = $response->getHeaderLine('Content-Type'); + + $this->logger->debug("Status Code: " . $statusCode); + $this->logger->debug("Content Type: " . $contentType); + + if ($statusCode === 200 && str_contains($contentType, 'image/')) { + // La respuesta es una imagen y el código de estado es 200 OK + $image_content = $response->getBody()->getContents(); + + // *** VERIFICACIÓN IMPORTANTE: *** + if (file_put_contents($output_filename, $image_content)) { + $this->logger->debug("¡Imagen guardada exitosamente en: " . $output_filename); + $url_file = $this->UploadVoucherToWordpressByImageFileName($voucherFileName); //Carga del comprobante PDF a Wordpress para su posterior envío + $arrayOxxoPayment['voucher_image_url'] = $url_file; // Agregar la URL del archivo de imagen al array + } else { + $this->logger->error("Error: No se pudo guardar la imagen en " . $output_filename); + $this->logger->error("Ruta del archivo: " . $output_filename); // Agregado para depuración + $this->logger->error("Directorio del script: " . __DIR__); // Agregado para depuración + $this->logger->error("Error de PHP: " . error_get_last()['message']); // Agregado para depuración + } + } else { + // No es una imagen o hubo un error en el servicio + $this->logger->debug("Error: La respuesta no es una imagen o el código de estado no es 200 OK."); + $this->logger->debug("Cuerpo de la respuesta: " . $response->getBody()->getContents()); + } + + } catch (RequestException $e) { + // Manejo de errores de red o del servidor (ej. timeouts, 4xx, 5xx si http_errors no es false) + $this->logger->error("Error en la petición: " . $e->getMessage()); + if ($e->hasResponse()) { + $this->logger->error("Cuerpo de la respuesta de error: " . $e->getResponse()->getBody()->getContents()); + $this->logger->error("Código de estado HTTP de error: " . $e->getResponse()->getStatusCode()); + } + } catch (Exception $e) { + // Otros errores inesperados + $this->logger->error("Error inesperado: " . $e->getMessage()); + } + + + + + return $arrayOxxoPayment; + } else { + $this->logger->info("El PaymentIntent no tiene detalles de OXXO disponibles. Estado: " . $paymentIntentConfirm['status'] . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorPaymentIntentWithoutOxxoDetails'; + $arrayOxxoPayment['failDescription'] = 'El PaymentIntent no tiene detalles de OXXO disponibles. Estado: ' . $paymentIntentConfirm['status']; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $amount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + + } + + } else { + $this->logger->info("Este cliente no tiene adeudos." . PHP_EOL); + //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount + $arrayOxxoPayment['oxxo_reference'] = ''; + $arrayOxxoPayment['url'] = ''; + $arrayOxxoPayment['error'] = 'errorsinadeudo'; + $arrayOxxoPayment['failDescription'] = 'Este cliente no tiene adeudos.'; + $arrayOxxoPayment['clientID'] = $clientID; + $arrayOxxoPayment['clientFullName'] = $clientFullName; + $arrayOxxoPayment['amount'] = $amount; + $arrayOxxoPayment['voucher_image_url'] = ''; + return $arrayOxxoPayment; + } + } + + + + + + + // /** + // * implement in subclass with the specific messaging provider + // * @see TwilioNotifierFacade::sendWhatsApp() + // */ + abstract protected function sendWhatsApp( + string $message + ): void; + + + + + function validarEmail($email) + { + $this->logger->debug('SE VALIDA EL EMAIL!!! ' . PHP_EOL); + // Utilizar la función filter_var con el filtro FILTER_VALIDATE_EMAIL para validar el email + if (filter_var($email, FILTER_VALIDATE_EMAIL)) { + // Si el email es válido, devolver el email + return $email; + } else { + // Si el email no es válido, devolver una cadena vacía + return ''; + } + } + + + function UploadReceiptToWordpressByImageFileName($imageFileName): string + { + + // $log = PluginLogManager::create(); //Initialize Logger + // $configManager = PluginConfigManager::create(); + // $config = $configManager->loadConfig(); + + // // Configuración de conexión FTP + // $ftp_server = $config['hostServerFTP']; + // $ftp_username = $config['usernameServerFTP']; + // $ftp_password = $config['passServerFTP']; + + $remote_folder = "/public_html/wp/wp-content/uploads/img/"; + + + // Configuración de conexión FTP + $ftp_server = "siip.mx"; + $ftp_username = "siip0001"; + $ftp_password = '$spGiT,[wa)n'; + $remote_file = "/public_html/wp/wp-content/uploads/img/" . $imageFileName; + $file_to_upload = __DIR__ . '/' . $imageFileName; + $url = 'https://siip.mx/wp/wp-content/uploads/img/' . $imageFileName; + + + + // Conexión FTP + $ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor FTP"); + $login = ftp_login($ftp_conn, $ftp_username, $ftp_password); + ftp_pasv($ftp_conn, true); + // Verificar conexión y login + if ($ftp_conn && $login) { + print_r("Conexión FTP exitosa" . PHP_EOL); + // $log->appendLog("Conexión FTP exitosa" . PHP_EOL); + + // Cargar archivo + if (ftp_put($ftp_conn, $remote_file, $file_to_upload, FTP_BINARY)) { + print_r("El archivo ha sido cargado exitosamente." . PHP_EOL); + print_r("La URL es: " . $url . PHP_EOL); + // $log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL); + // $log->appendLog("La URL es: " . $url . PHP_EOL); + // Cerrar conexión FTP + //ftp_close($ftp_conn); + //return $url; + + } else { + //$log->appendLog("Error al cargar el archivo " . PHP_EOL); + print_r("Error al cargar el archivo " . PHP_EOL); + ftp_close($ftp_conn); + return ''; + } + + // Obtener lista de archivos en la carpeta + $files = ftp_nlist($ftp_conn, $remote_folder); + if (is_array($files)) { + // Eliminar la ruta del directorio de los archivos + $files = array_map(function ($file) use ($remote_folder) { + return str_replace($remote_folder, '', $file); + }, $files); + + // Obtener fechas de modificación + $filesWithTime = []; + foreach ($files as $file) { + $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); + if ($modifiedTime != -1) { + $filesWithTime[$file] = $modifiedTime; + } + } + + // Ordenar archivos por fecha de modificación, más recientes primero + arsort($filesWithTime); + + // Obtener los archivos a eliminar (todos menos los 5 más recientes) + $filesToDelete = array_slice(array_keys($filesWithTime), 5); + + // Eliminar archivos antiguos + foreach ($filesToDelete as $file) { + if (ftp_delete($ftp_conn, $remote_folder . $file)) { + print_r("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); + // $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); + } else { + print_r('Error al borrar comprobante' . $file . PHP_EOL); + // $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL); + } + } + print_r("Archivos eliminados" . PHP_EOL); + // $log->appendLog("Archivos eliminados" . PHP_EOL); + ftp_close($ftp_conn); + return $url; + } else { + print_r("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); + // $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); + ftp_close($ftp_conn); + return $url; + } + + // Cerrar conexión FTP + //ftp_close($ftp_conn); + //return ''; + } else { + print_r("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); + // $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); + return ''; + } + + } + + + + function UploadVoucherToWordpressByImageFileName($imageFileName): string + { + + $log = PluginLogManager::create(); //Initialize Logger + $configManager = PluginConfigManager::create(); + $config = $configManager->loadConfig(); + + // Configuración de conexión FTP + $ftp_server = $config['hostServerFTP']; + $ftp_username = $config['usernameServerFTP']; + $ftp_password = $config['passServerFTP']; + $remote_folder = "/public_html/wp/wp-content/uploads/vouchers_oxxo/"; + + $log->appendLog("Subiendo voucher a worpdpress " . PHP_EOL); + // Configuración de conexión FTP + // $ftp_server = "siip.mx"; + // $ftp_username = "siip0001"; + // $ftp_password = '$spGiT,[wa)n'; + $remote_file = "/public_html/wp/wp-content/uploads/vouchers_oxxo/" . $imageFileName; + $file_to_upload = __DIR__ . '/../../vouchers_oxxo/' . $imageFileName; + $url = 'https://siip.mx/wp/wp-content/uploads/vouchers_oxxo/' . $imageFileName; + + $log->appendLog("file_to_upload: " . $file_to_upload . PHP_EOL); + + // Conexión FTP + $ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor FTP"); + $login = ftp_login($ftp_conn, $ftp_username, $ftp_password); + ftp_pasv($ftp_conn, true); + // Verificar conexión y login + if ($ftp_conn && $login) { + $log->appendLog("Conexión FTP exitosa" . PHP_EOL); + + // Cargar archivo + if (ftp_put($ftp_conn, $remote_file, $file_to_upload, FTP_BINARY)) { + $log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL); + $log->appendLog("La URL es: " . $url . PHP_EOL); + + $this->deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_file); + // Cerrar conexión FTP + ftp_close($ftp_conn); //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO + return $url; //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO + + } else { + $log->appendLog("Error al cargar el archivo " . PHP_EOL); + ftp_close($ftp_conn); + return ''; + } + + //SI SE DECIDE VOLVER A ELIMINAR LOS COMPROBANTES ENTONCES DESCOMENTAR ESTA PARTE DE ABAJO Y COMENTAR LA SECCIÓN DE ARRIBA + + // Obtener lista de archivos en la carpeta + // $files = ftp_nlist($ftp_conn, $remote_folder); + // if (is_array($files)) { + // // Eliminar la ruta del directorio de los archivos + // $files = array_map(function ($file) use ($remote_folder) { + // return str_replace($remote_folder, '', $file); + // }, $files); + + // // Obtener fechas de modificación + // $filesWithTime = []; + // foreach ($files as $file) { + // $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); + // if ($modifiedTime != -1) { + // $filesWithTime[$file] = $modifiedTime; + // } + // } + + // // Ordenar archivos por fecha de modificación, más recientes primero + // arsort($filesWithTime); + + // // Obtener los archivos a eliminar (todos menos los 50 más recientes) + // $filesToDelete = array_slice(array_keys($filesWithTime), 50); + + // // Eliminar archivos antiguos + // foreach ($filesToDelete as $file) { + // if (ftp_delete($ftp_conn, $remote_folder . $file)) { + // $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); + // } else { + // $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL); + // } + // } + // $log->appendLog("Archivos eliminados" . PHP_EOL); + // ftp_close($ftp_conn); + // return $url; + // } else { + // $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); + // ftp_close($ftp_conn); + // return $url; + // } + + } else { + $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); + return ''; + } + } + + function deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_folder): bool + { + + // Obtener lista de archivos en la carpeta + $files = ftp_nlist($ftp_conn, $remote_folder); + if (is_array($files)) { + // Eliminar la ruta del directorio de los archivos + $files = array_map(function ($file) use ($remote_folder) { + return str_replace($remote_folder, '', $file); + }, $files); + + // Obtener fechas de modificación + $filesWithTime = []; + foreach ($files as $file) { + $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); + if ($modifiedTime != -1) { + $filesWithTime[$file] = $modifiedTime; + } + } + + // Ordenar archivos por fecha de modificación, más recientes primero + arsort($filesWithTime); + + // Obtener los archivos a eliminar (todos menos los 50 más recientes) + $filesToDelete = array_slice(array_keys($filesWithTime), 50); + + // Eliminar archivos antiguos + foreach ($filesToDelete as $file) { + if (ftp_delete($ftp_conn, $remote_folder . $file)) { + $log->appendLog("Voucher eliminado de Wordpress: " . $file . PHP_EOL); + } else { + $log->appendLog('Error al borrar voucher' . $file . PHP_EOL); + } + } + $log->appendLog("Archivos eliminados" . PHP_EOL); + ftp_close($ftp_conn); + return true; + } else { + $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); + ftp_close($ftp_conn); + return false; + } + } + +} diff --git a/archivos_webhook_destino/src/Facade/AbstractStripeOperationsFacade.php b/archivos_webhook_destino/src/Facade/AbstractStripeOperationsFacade.php new file mode 100755 index 0000000..e83882d --- /dev/null +++ b/archivos_webhook_destino/src/Facade/AbstractStripeOperationsFacade.php @@ -0,0 +1,489 @@ +logger = $logger; + $this->messageTextFactory = $messageTextFactory; + $this->clientPhoneNumber = $clientPhoneNumber; + } + + /* + * Creates a PaymentIntent in Stripe for a Customer + */ + public function createPaymentIntent($event_json) + { + $this->logger->info("Iniciando creación de PaymentIntent en Stripe." . PHP_EOL); + + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + $StripeToken = $config['tokenstripe']; + $idPaymentAdmin = $config['idPaymentAdminCRM']; //ID del administrador que crea el PaymentIntent + $stripe = new \Stripe\StripeClient($StripeToken); //Token de clave privada para la API de Stripe + + // Asegurarse de que 'customer' esté presente en la estructura del evento + if (!isset($event_json['data']['object']['customer'])) { + $this->logger->info("Error: Invalid event structure. Customer ID not found." . PHP_EOL); + return; + } + + $customerId = $event_json['data']['object']['customer']; + if (is_null($customerId)) { + $this->logger->info("Error: Customer ID is null." . PHP_EOL); + return; + } + + // Validar que net_amount está presente + if (!isset($event_json['data']['object']['net_amount'])) { + $this->logger->info("Error: net_amount not found in event data." . PHP_EOL); + return; + } + + $amount = $event_json['data']['object']['net_amount']; + //convertir en positivo + if ($event_json['data']['object']['net_amount'] < 0) { + $this->logger->warning("Error: net_amount es negativo." . PHP_EOL); + return; + + } + + + //imprimir la cantidad del PaymentIntent + $this->logger->info("Cantidad del PaymentIntent: " . $amount . PHP_EOL); + + try { + // Obtener información del cliente desde Stripe + $stripeQuery = $stripe->customers->retrieve($customerId, []); + $UCRM_clientID = $stripeQuery['metadata']['ucrm_client_id']; // ID del cliente en Ubiquiti UISP + + // Obtener información del administrador actual + $this->ucrmApi = UcrmApi::create(); + $currentUserAdmin = $this->ucrmApi->get('users/admins', []); + + // Crear PaymentIntent + $paymentIntent = $stripe->paymentIntents->create([ + 'amount' => $amount, + 'currency' => 'mxn', + 'customer' => $customerId, + 'payment_method_types' => ['customer_balance'], + 'payment_method_data' => ['type' => 'customer_balance'], + 'confirm' => true, + 'payment_method_options' => [ + 'customer_balance' => [ + 'funding_type' => 'bank_transfer', + 'bank_transfer' => ['type' => 'mx_bank_transfer'] + ], + ], + 'metadata' => [ + 'clientId' => $UCRM_clientID, // ID del cliente en Ubiquiti + 'createdBy' => 'UCRM', + 'paymentType' => 'card.one_time', + 'signedInAdminId' => $idPaymentAdmin, // ID del administrador que crea el PaymentIntent + 'tipoPago' => 'Transferencia Bancaria' + ], + ]); + + $this->logger->info("PaymentIntent creado: " . $paymentIntent->id . PHP_EOL); + + } catch (\Stripe\Exception\ApiErrorException $e) { + $this->logger->info("Error creando PaymentIntent: " . $e->getMessage() . PHP_EOL); + } + } + + + /* + * Creates the Stripe Customer + */ + public function createStripeClient(NotificationData $notificationData, $tagStripe = false): void + { + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + // the "exportFormat" key must be defined in plugin's manifest file, see the link above + $IPServer = $config['ipserver']; + $UCRMAPIToken = $config['apitoken']; + $StripeToken = $config['tokenstripe']; + $baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; //endpoint de la API REST del CRM + $this->ucrmApi = UcrmApi::create(); + + $stripe = new \Stripe\StripeClient($StripeToken); //Token de clave privada en modo prueba para la API de Stripe + //$stripe = new \Stripe\StripeClient('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'); //Token de clave privada en modo prodcucción para la API de Stripe + + //valor de $notificationData en json + $this->logger->info("Valor de notificationData: " . json_encode($notificationData) . PHP_EOL); + + //ejemplo de notificationData: {"uuid":"0be6fee6-db1d-4ab5-a52c-2ee87b04315e","changeType":"edit","entity":"client","entityId":170,"message":null,"clientId":170,"eventName":"client.edit","clientData":{"id":170,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"San Luis 34","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"San Luis 34, Dolores Hidalgo, 37800","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-03-24T00:00:00-0600","leadConvertedAt":"2025-03-24T15:01:22-0600","companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Charicuas","lastName":"Quinero","username":null,"contacts":[{"id":176,"clientId":170,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[{"id":192,"clientId":170,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_S0T9BJ4dO0p0A3","clientZoneVisible":true},{"id":193,"clientId":170,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"124180302040274015","clientZoneVisible":true}],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[{"id":5,"name":"CREARCLABESTRIPE","colorBackground":"#e30000","colorText":"#fff"}],"invitationEmailSentDate":null,"avatarColor":"#ef5350","addressGpsLat":22.00854045,"addressGpsLon":-99.0272544,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":true,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"serviceData":null,"invoiceData":null,"paymentData":null} + + //obtener el id del cliente + $clientId = $notificationData->clientData['id']; + + //si en attributes del cliente encuentra el custom field de Stripe Customer ID entonces no se vuelve a crear el cliente en Stripe + $attributes = $notificationData->clientData['attributes']; + $this->logger->info("Valor de attributes: " . json_encode($attributes) . PHP_EOL); + $stripeCustomerId = null; + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'stripeCustomerId') { + $stripeCustomerId = $attribute['value']; + break; + } + } + + $customAttributes = $this->ucrmApi->get('custom-attributes/', ['attributeType' => 'client']);//Obtener los atributos del sistema que estén vinculados a la entidad "cliente" + //$this->logger->info("result del custom Attributes: " . json_encode($customAttributes) . PHP_EOL); + + + // Verificar si se obtuvieron los atributos + if ($customAttributes && is_array($customAttributes)) { + foreach ($customAttributes as $attribute) { + // Verificar si 'name' contiene la palabra 'Stripe' sin distinguir mayúsculas y minúsculas + if (isset($attribute['name']) && stripos($attribute['name'], 'Stripe') !== false) { + $this->logger->info("ID correspondiente a 'Customer Stripe ID': " . $attribute['id'] . PHP_EOL); + $this->stripeCustomAttributeID = $attribute['id']; + } else if (isset($attribute['name']) && stripos($attribute['name'], 'Clabe') !== false) { + //$this->logger->info("ID correspondiente a 'Clabe Interbancaria Banamex': " . $attribute['id'] .PHP_EOL); + $this->clabeInterbancariaBanamexID = $attribute['id']; + } + + + } + } else { + $this->logger->info("Error al obtener los atributos personalizados." . PHP_EOL); + } + + if ($stripeCustomerId) { + $this->logger->info("El cliente ya tiene un Stripe Customer ID: " . $stripeCustomerId . PHP_EOL); + if ($tagStripe) { + $tagsIds = $this->ucrmApi->get('client-tags', []); + //ejemplo de respuesta $tagsIds: [{"id":4,"name":"EQUIPO A CREDITO","colorBackground":"#fed74a","colorText":"#444"},{"id":5,"name":"CREARCLABESTRIPE","colorBackground":"#e30000","colorText":"#fff"}] + + //obtener el ID de la etiqueta o tag llamada "CREAR CLABE STRIPE" y asiganarla a una variable $tagCrearClabeStripe basandose en el ejemplo de respuesta anterior + $tagCrearClabeStripe = null; + foreach ($tagsIds as $tag) { + if ($tag['name'] === 'CREAR CLABE STRIPE') { + $tagCrearClabeStripe = $tag['id']; + // $this->logger->info("ID de la etiqueta 'CREAR CLABE STRIPE': " . $tagCrearClabeStripe . PHP_EOL); + break; + } + } + if (!$tagCrearClabeStripe) { + $this->logger->info("No se encontró la etiqueta 'CREAR CLABE STRIPE'." . PHP_EOL); + return; + } + + $this->ucrmApi->patch("clients/$clientId/remove-tag/" . $tagCrearClabeStripe); + $this->logger->info("Se eliminó la etiqueta 'CREAR CLABE STRIPE' del cliente." . PHP_EOL); + + } + return; + } + + //$this->logger->info("Ya dentro del metodo Create Stripe Client " . PHP_EOL); + //$this->sendWhatsApp('Hola Dany'); + + + if ($tagStripe) { + $tagsIds = $this->ucrmApi->get('client-tags', []); + //ejemplo de respuesta $tagsIds: [{"id":4,"name":"EQUIPO A CREDITO","colorBackground":"#fed74a","colorText":"#444"},{"id":5,"name":"CREARCLABESTRIPE","colorBackground":"#e30000","colorText":"#fff"}] + + //obtener el ID de la etiqueta o tag llamada "CREARCLABESTRIPE" y asiganarla a una variable $tagCrearClabeStripe basandose en el ejemplo de respuesta anterior + $tagCrearClabeStripe = null; + foreach ($tagsIds as $tag) { //revisamos los tags del cliente y si encuentra el tag de "CREARCLABESTRIPE" entonces lo asigna a la variable $tagCrearClabeStripe el valor de id + if ($tag['name'] === 'CREAR CLABE STRIPE') { + $tagCrearClabeStripe = $tag['id']; + // $this->logger->info("ID de la etiqueta 'CREAR CLABE STRIPE': " . $tagCrearClabeStripe . PHP_EOL); + break; + } + } + if (!$tagCrearClabeStripe) { + $this->logger->info("No se encontró la etiqueta 'CREAR CLABE STRIPE'." . PHP_EOL); + return; + } + $this->createCustomerStripe($notificationData, $stripe, $baseUri, $UCRMAPIToken); + $this->ucrmApi->patch("clients/$clientId/remove-tag/" . $tagCrearClabeStripe); + $this->logger->info("Se eliminó la etiqueta 'CREAR CLABE STRIPE' del cliente." . PHP_EOL); + return; + } + + + + + + + // Verificar si la solicitud fue exitosa (código de estado 200) + //https://172.16.5.120/crm/api/v1.0/payments?limit=1&clientId=1992&order=createdDate&direction=DESC + + $notification_client_data = $notificationData->clientData; //array con los datos del cliente + + // Asegúrate de trabajar con un objeto PHP + $dataObject = json_decode(json_encode($notification_client_data)); // Convertir a JSON y luego decodificar como objeto + + if (!is_object($dataObject)) { + $this->logger->info("Error: Los datos del cliente no pudieron ser procesados." . PHP_EOL); + return; + } + + $this->createCustomerStripe($notificationData, $stripe, $baseUri, $UCRMAPIToken); + } + + // /** + // * implement in subclass with the specific messaging provider + // * @see TwilioNotifierFacade::sendWhatsApp() + // */ + abstract protected function sendWhatsApp( + string $message + ): void; + + + function createCustomerStripe($notificationData, $stripe, $baseUri, $token): bool + { + $this->ucrmApi = UcrmApi::create(); + $this->logger->info("Creando el Customer Stripe" . PHP_EOL); + + + + $ucrm_client_id = $notificationData->clientData['id']; + $clientCRMContacts = $notificationData->clientData['contacts']; + + $firstName = $notificationData->clientData['firstName']; //obtenemos nombre del cliente y lo almacenamos en una variable + $lastName = $notificationData->clientData['lastName']; //obtenemos apellidos del cliente y lo almacenamos en una variable + $this->logger->info('El cliente a procesar es : ' . $firstName . ' ' . $lastName . PHP_EOL); //impresión de control para ver el nombre del cliente a procesar en consola + + $cadenaNotificationData = json_encode($notificationData); + $this->logger->info("Datos notificationData: " . $cadenaNotificationData . PHP_EOL); + foreach ($clientCRMContacts as $contact) { //aquí revisamos los datos de contacto del cliente, como pueden ser uno o varios se hace uso del foreach + $this->logger->info('Ya dentro del FOREACH!!! ' . PHP_EOL); + + $phone = ''; //variable para almacenar el número de teléfono del cliente que se mandará a su cuenta de Stripe + + foreach ($contact['types'] as $type) { //revisamos el tipo de contacto + + $this->logger->info('REVISANDO EL PRIMER CONTACTO!!! ' . PHP_EOL); + if ($type['name'] === "WhatsApp") { //si es de tipo whatsapp.. + //print_r("Encontré un tipo de Contacto para WhatsAapp" . PHP_EOL); + $phone = $contact['phone']; //se asigna como número de teléfono + break; + } else { + $phone = $contact['phone']; //Si no encuentra un tipo de contacto como Whatsapp entonces el último número de celular obtenido es el que se envía + } + } + $email = $this->validarEmail($contact['email']); //validamos el email del cliente mediante la función validarEmail para que en caso de que no esté bien formado lo ponga en blanco + + + + } + + $this->logger->info('ahora se procede a validar el teléfono!!! ' . PHP_EOL); + + //si la variable $phone no esta vacia o null se valida con la funcion validarNumeroTelefono + if ($phone == '') { + $this->logger->info('El número de teléfono está vacío o no es válido.' . PHP_EOL); + //se manda a crear pero sin numero de teléfono + $this->logger->info('AHORA SE MANDA A CREAR EL CLIENTE A STRIPE!!! ' . PHP_EOL); + $result = $stripe->customers->create([ + "description" => "Cliente SIIP CRM con client_id: " . $ucrm_client_id, + "name" => $firstName . ' ' . $lastName, + "email" => $email, + 'preferred_locales' => ['es-419'] + ]); //aquí se contruye la petición de creación de un customer en Stripe y se consume la API + + } else { + $phone = $this->validarNumeroTelefono($phone); //validamos y procesamos el número de celular del cliente para que se vaya a 10 dígitos ya que Stripe por si mismo les agrega el +52 + $this->logger->info('AHORA SE MANDA A CREAR EL CLIENTE A STRIPE!!! ' . PHP_EOL); + $result = $stripe->customers->create([ + "description" => "Cliente SIIP CRM con client_id: " . $ucrm_client_id, + "name" => $firstName . ' ' . $lastName, + "phone" => $phone, + "email" => $email, + 'preferred_locales' => ['es-419'] + ]); //aquí se contruye la petición de creación de un customer en Stripe y se consume la API + + } + + $this->logger->info(json_encode($result) . PHP_EOL); //imprimir respuesta de creación del cliente + //ejemplo de respuesta de stripe cuando se crea el cliente: {"id":"cus_Rkh9CoRTpjlZUu","object":"customer","address":null,"balance":0,"created":1739250682,"currency":null,"default_source":null,"delinquent":false,"description":"Cliente SIIP CRM con client_id: 167","discount":null,"email":null,"invoice_prefix":"5017E9D3","invoice_settings":{"custom_fields":null,"default_payment_method":null,"footer":null,"rendering_options":null},"livemode":false,"metadata":[],"name":"Javier Alatorre","next_invoice_sequence":1,"phone":"4181878106","preferred_locales":["es-419"],"shipping":null,"tax_exempt":"none","test_clock":null} + + //validar si se creo el cliente con la API de Stripe $stripe->customers->create + if (!isset($result['id'])) { //si no existe el ID del cliente en la respuesta de Stripe entonces se manda un mensaje de error + $this->logger->info('Error al crear el cliente en Stripe: ' . json_encode($result) . PHP_EOL); + + } + + + + $stripe_customer_id = $result['id']; //obtenemos el customer id de Stripe recibido por medio del consumo del API y lo asignamos a una variable $stripe_customer_id + sleep(2); + $result2 = $stripe->customers->update( + $stripe_customer_id, + ['metadata' => ['ucrm_client_id' => '' . $ucrm_client_id]] + );//aquí se contruye la petición de actualización de un customer en Stripe para agregarle los metadatos y se consume la API, requiere el ID del Customer, por eso antes lo almacenamos en la variable $stripe_customer_id + + $this->logger->info(json_encode($result2) . PHP_EOL); // imprimir respuesta de actualización del metadata del cliente + sleep(2); + $result3 = $stripe->customers->createFundingInstructions( + $stripe_customer_id, + [ + 'currency' => 'mxn', + 'funding_type' => 'bank_transfer', + 'bank_transfer' => ['type' => 'mx_bank_transfer'], + ] + ); //aquí se contruye la petición de creación de Instrucciones de Fondeo de un customer en Stripe (O su metodo de pago como cuenta bancaria para transferencias) y se consume la API + + $this->logger->info(json_encode($result3) . PHP_EOL); //imprimir respuesta de asignación de cuenta bancaria de transferencia + + // Acceder al valor de "clabe" para BANORTE + $clabeInterbancaria = $result3['bank_transfer']['financial_addresses'][0]['spei']['clabe']; //Asignamos la clabe obtenida con la API de Stripe con la solicitud anterior a la variable $clabe + $stripeID = $this->stripeCustomAttributeID; + $clabeInterbancariaID = $this->clabeInterbancariaBanamexID; + + $customer = $stripe->customers->update( + $stripe_customer_id, + [ + 'metadata' => [ + 'clabe' => $clabeInterbancaria, // Nueva clabe + ], + ] + ); + + $this->logger->info("CLABE guardada en metadata: " . $customer->metadata->clabe . PHP_EOL); + + + $json_data_patch = '{ + "attributes": [ + { + "value": "' . $stripe_customer_id . '", + "customAttributeId":' . $stripeID . ' + }, + { + "value": "' . $clabeInterbancaria . '", + "customAttributeId":' . $clabeInterbancariaID . ' + } + ] + + }'; //JSON para hacer patch de los custom fields del cliente en el UISCP CRM, Campo para el Stripe Customer ID y la Clabe interbancaria + + //valor de $json_data_patch en json + //$this->logger->info("Valor de json_data_patch: " . $json_data_patch . PHP_EOL); + // try{ + // $responsepatchCRM= $this->ucrmApi->patch("clients/$ucrm_client_id", [ + // "attributes" => [ + // [ + // "value" => $stripe_customer_id, + // "customAttributeId" => $stripeID + // ], + // [ + // "value" => $clabeInterbancaria, + // "customAttributeId" => $clabeInterbancariaID + // ] + // ] + // ]); //aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM + // $this->logger->info("Se actualizó el cliente en UCRM con el Stripe Customer ID y la Clabe Interbancaria." . PHP_EOL); + // $this->logger->info(json_encode($responsepatchCRM) . PHP_EOL); //imprimir respuesta del patch de CRM con la clabe y Customer ID Stripe + // }catch (GuzzleException $e) { + // $this->logger->info("Error al hacer el patch al cliente en UCRM: " . $e->getMessage() . PHP_EOL); + // return false; // Return false if patch fails + // } + + + $clientguzz = new Client(); //instancia de cliente GuzzleHttp para consumir API UISP CRM + try { + //aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM + $responseCRM = $clientguzz->patch($baseUri . 'clients/' . $ucrm_client_id, [ + 'json' => json_decode($json_data_patch, true), + 'headers' => [ + 'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización + 'Accept' => 'application/json', // Indica que esperamos una respuesta en formato JSON + ], + 'verify' => false, + ]); + + + } catch (GuzzleException $error) { + $this->logger->info("Error al hacer el patch al CRM: " . $error->getMessage() . PHP_EOL); + return false; // Return false if patch fails + } + $this->logger->info(json_encode($responseCRM) . PHP_EOL); //imprimir respuesta del patch de CRM con la clabe y Customer ID Stripe + + return true; // Return true if all operations succeed + } + + function validarNumeroTelefono($telefono) + { + // Eliminar espacios y guiones + $telefono = preg_replace('/\s+|-/', '', $telefono); + + // Eliminar caracteres no numéricos + $telefono = preg_replace('/\D/', '', $telefono); + + // Verificar si quedan exactamente 10 dígitos + if (strlen($telefono) === 10) { + // Retornar el número de teléfono correctamente formateado + return $telefono; + } elseif (strlen($telefono) > 10) { + // Si el número tiene más de 10 dígitos, quitar los primeros + return substr($telefono, -10); + } else { + // Si el número tiene menos de 10 dígitos, retornar cadena vacía + return ''; + } + } + + function validarEmail($email) + { + $this->logger->info('SE VALIDA EL EMAIL!!! ' . PHP_EOL); + // Utilizar la función filter_var con el filtro FILTER_VALIDATE_EMAIL para validar el email + if (filter_var($email, FILTER_VALIDATE_EMAIL)) { + // Si el email es válido, devolver el email + return $email; + } else { + // Si el email no es válido, devolver una cadena vacía + return ''; + } + } + + + +} diff --git a/archivos_webhook_destino/src/Facade/AbstractUpdateClientFacade.php b/archivos_webhook_destino/src/Facade/AbstractUpdateClientFacade.php new file mode 100755 index 0000000..54ab925 --- /dev/null +++ b/archivos_webhook_destino/src/Facade/AbstractUpdateClientFacade.php @@ -0,0 +1,98 @@ +logger = $logger; + $this->messageTextFactory = $messageTextFactory; + $this->smsNumberProvider = $smsNumberProvider; + } + + /* + * sets up the body and uses the implementation's sendMessage() to send + */ + public function notify(NotificationData $notificationData): void + { + $clientSmsNumber = $this->smsNumberProvider->getUcrmClientNumber($notificationData); //renombrar variables + + if (empty($clientSmsNumber)) { + $this->logger->warning('No SMS number found for client: ' . $notificationData->clientId); + return; + } + // $messageBody = $this->messageTextFactory->createBody($notificationData); + // if (! $messageBody) { + // $this->logger->info('No text configured for event: ' . $notificationData->eventName); + // return; + // } + + + try { + $this->logger->debug(sprintf('llego al llamado de sendwhatsapp con client_id: %s y número de celular: %s', $notificationData->clientId, $clientSmsNumber)); + //$this->sendMessage($notificationData, $clientSmsNumber, $messageBody); + //$this->sendWhatsApp($notificationData, $clientSmsNumber); + + $client_callbell_api = new ClientCallBellAPI(); + $client_callbell_api->sendMessageWhatsApp($clientSmsNumber, $notificationData); + $response_getContactCallBell=json_decode($client_callbell_api->getContactWhatsapp($clientSmsNumber)); + $client_callbell_api->patchWhatsapp($response_getContactCallBell->contact->uuid,$notificationData); + + } catch (HttpException $httpException) { + //$this->logger->debug('Ocurrio un error en el try catch'); + $this->logger->error($httpException->getCode() . ' ' . $httpException->getMessage()); + } + } + + /** + * implement in subclass with the specific messaging provider + * @see TwilioNotifierFacade::sendMessage() + */ + abstract protected function sendMessage( + NotificationData $notificationData, + string $clientSmsNumber, + string $messageBody + ): void; + + // /** + // * implement in subclass with the specific messaging provider + // * @see TwilioNotifierFacade::sendWhatsApp() + // */ + // abstract protected function sendWhatsApp( + // NotificationData $notificationData, + // string $clientSmsNumber + // ): void; +} diff --git a/archivos_webhook_destino/src/Facade/ClientCallBellAPI.php b/archivos_webhook_destino/src/Facade/ClientCallBellAPI.php new file mode 100755 index 0000000..f9bbbdb --- /dev/null +++ b/archivos_webhook_destino/src/Facade/ClientCallBellAPI.php @@ -0,0 +1,1346 @@ +UCRMAPIToken = $UCRMAPIToken; + $this->IPServer = $IPServer; + $this->CallBellAPIToken = $CallBellAPIToken; + } + + public function updateContact($client_uuid) + { + } + + public function printPrueba($clientWhatsAppNumber, $notificationData) + { + $log = PluginLogManager::create(); //Initialize Logger + $log->appendLog("Ejecutando metodo print prueba: " . $clientWhatsAppNumber); + // $logger = new Logger(); + // $logger->debug('Ejecutando metodo print prueba con Logger'); + } + + public function sendMessageWhatsApp($clientWhatsAppNumber, $notificationData) + { + + $log = PluginLogManager::create(); //Initialize Logger + $log->appendLog("Entrando al metodo sendMessageWhatsApp" . PHP_EOL); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer g8thcZkXGd3xBj2g3TtYNYFMH1fuesbJ.b6a940ea7d78cf6c9e42f067b21c8ddf96e9fa2a9e307bfd0c7c7c4d7fa38f79', + 'Content-Type: application/json', + ]); + + // $jsonString = json_encode($notificationData); + // $log->appendLog($jsonString); + + $campo1 = sprintf("😀 *%s %s*", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); + $log->appendLog("Valor del campo1: " . $campo1); + $estado_service = ($notificationData->clientData['hasSuspendedService']) ? 'Suspendido 🔴' : 'Activo 🟢'; + $log->appendLog("Valor del estado_service: " . $estado_service); + $domicilio = ($notificationData->clientData['fullAddress'] == null) ? 'Sin domicilio' : $notificationData->clientData['fullAddress']; + $log->appendLog("Valor del domicilio: " . $domicilio); + $campo2 = sprintf("📡 Su servicio está: %s 📍Su dirección es: *%s* ", $estado_service, $domicilio); + $log->appendLog("Valor del campo2 " . $campo2); + + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }"; + $log->appendLog("La cadena CURL que se envia es: " . $curl_string); + curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); + + $response = curl_exec($ch); + $log->appendLog("Response del CallBell: " . $response); + + curl_close($ch); + } + + public function sendJobNotificationWhatsAppToClient($clientWhatsAppNumber, $jobNotificationData, $reprogramming, $changeInstaller): bool + { + + $log = PluginLogManager::create(); //Initialize Logger + $log->appendLog("Enviando mensaje de trabajo para el cliente" . PHP_EOL); + $jsonJobNotificationData = json_encode($jobNotificationData, true); + $log->appendLog("Datos de la notificación de trabajo: " . $jsonJobNotificationData . PHP_EOL); + + // --- ¡AÑADE ESTAS LÍNEAS PARA CONVERTIR A BOOLEANO REAL! --- + $reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + $changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + // ----------------------------------------------------------- + + // Puedes volver a loggear los valores para confirmar la conversión (opcional) + $log->appendLog("DEBUG: Valores después de conversión - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + + $campo1 = '👤 ' . sprintf(' *%s*', $jobNotificationData['clientFullName']); + if ($changeInstaller && !$reprogramming) { + $campo2 = sprintf('se ha hecho un cambio de técnico 👷🏻‍♂️🔄 para su visita con el folio *#️⃣%s*', $jobNotificationData['jobId']); + } else { + $campo2 = sprintf('*#️⃣%s*', $jobNotificationData['jobId']); + } + $campo3 = '🗓️ ' . sprintf('%s', $jobNotificationData['date']); + if ($changeInstaller && $reprogramming) { + $campo4 = 'Además se ha hecho un cambio de técnico por el siguiente 👷🏻‍♂️➡️ ' . sprintf('*%s*', $jobNotificationData['installerName']); + } else { + $campo4 = '👷🏻‍♂️➡️ ' . sprintf('*%s*', $jobNotificationData['installerName']); + } + + $campo5 = "asegúrese de que alguien esté presente en el domicilio 🏠 para permitir el acceso y confirme su disponibilidad."; + + $log->appendLog("DEBUG: Valores antes de la estructura IF - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL); + + if ($reprogramming && !$changeInstaller) { + //{{1}}, se ha reprogramado su visita técnica con el folio {{2}} + + $campo3 = '🗓️➡️ ' . sprintf('%s', $jobNotificationData['date']); + $campo1_combinado = "Estimado cliente $campo1 se ha reprogramado su visita técnica con folio $campo2"; + + // Case: true, false + //Enviar notificación de reprogramación al cliente + $log->appendLog("Enviando notificación de reprogramación al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"715eed9d6f2d4d90853f25e296202e00\",\n \"optin_contact\": true\n }"; + } else if (!$reprogramming && $changeInstaller) { + // Case: false, true + //Enviar notificación de cambio de instalador + $log->appendLog("Enviando notificación de cambio de instalador al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"d684e6fa2ba24593a86c98be1815831d\",\n \"optin_contact\": true\n }"; + } else if (!$reprogramming && !$changeInstaller) { // <--- Ahora este else if está correctamente encadenado + // Case: false, false + //Enviar notificación normal de visita técnica al cliente + $log->appendLog("Enviando notificación normal de visita técnica al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"07cfbc6e394044608485b530a27177d0\",\n \"optin_contact\": true\n }"; + } else if ($reprogramming && $changeInstaller) { // <--- Ahora este else if está correctamente encadenado + // Case: true, true + //Enviar notificación de cambio de instalador y reprogramación al cliente + $log->appendLog("Enviando notificación de cambio de instalador y reprogramación al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"145885d15323414f978f1e3f249c2ae1\",\n \"optin_contact\": true\n }"; + } else { + // Case: true, true (la única combinación restante con booleanos) + $log->appendLog("No se encontró una opción válida para enviar la notificación (reprogramming y changeInstaller son true)." . PHP_EOL); + // Decide qué hacer aquí, ¿Quizás devolver false? ¿O manejar esta combinación? + return false; // O el manejo adecuado para true/true + } + + $log->appendLog("La cadena CURL que se envia es: " . $curl_string); + curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); + + $response = curl_exec($ch); + $log->appendLog("Response del CallBell: " . $response); + + curl_close($ch); + + // Validar la respuesta de Callbell + $jsonResponse = json_decode($response, true); + + if (json_last_error() === JSON_ERROR_NONE) { + // Evaluar si contiene "message" con "status": "enqueued" + if (isset($jsonResponse['message']) && $jsonResponse['message']['status'] === 'enqueued') { + $log->appendLog("La notificación fue enviada correctamente con estado: enqueued" . PHP_EOL); + return true; + } + + // Evaluar si contiene "error" + if (isset($jsonResponse['error'])) { + $log->appendLog("Error al enviar la notificación: " . json_encode($jsonResponse['error']) . PHP_EOL); + return false; + } + } else { + // Manejo de error en caso de que la respuesta no sea un JSON válido + $log->appendLog("La respuesta no es un JSON válido." . PHP_EOL); + return false; + } + + // Valor de retorno predeterminado en caso de que ninguna condición se cumpla + $log->appendLog("Ruta no prevista en la función." . PHP_EOL); + return false; + } + + public function sendJobNotificationWhatsAppToInstaller($installerWhatsAppNumber, $jobInstallerNotificationData, $reprogramming, $changeInstaller): bool + { + + $log = PluginLogManager::create(); //Initialize Logger + $log->appendLog("Enviando mensaje de tarea al instalador" . PHP_EOL); + $jsonJobNotificationData = json_encode($jobInstallerNotificationData, true); + $log->appendLog("Datos de la notificación de tarea: " . $jsonJobNotificationData . PHP_EOL); + + // --- ¡AÑADE ESTAS LÍNEAS PARA CONVERTIR A BOOLEANO REAL! --- + $reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + $changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + // ----------------------------------------------------------- + + // Puedes volver a loggear los valores para confirmar la conversión (opcional) + $log->appendLog("DEBUG: Valores después de conversión - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + + + if (!$reprogramming && $changeInstaller) { + $campo1 = $jobInstallerNotificationData['installerName']; + $campo2 = $jobInstallerNotificationData['subjectOfChange']; + $campo3 = sprintf("#️⃣%s", $jobInstallerNotificationData['jobId']); + $campo4 = sprintf('*👤%s* ', $jobInstallerNotificationData['clientFullName']); + $campo5 = $jobInstallerNotificationData['additionalChangeData']; + } else { + $campo1 = '👷🏻‍♂️ ' . $jobInstallerNotificationData['installerName']; + $campo2 = sprintf("#️⃣%s", $jobInstallerNotificationData['jobId']); + $campo3 = '👤 *' . $jobInstallerNotificationData['clientFullName'] . '*'; + // $campo4 = $jobInstallerNotificationData['clientAddress']; + $campo4 = '☎️ ' . $jobInstallerNotificationData['clientWhatsApp']; + $campo5 = '🗓️ ' . $jobInstallerNotificationData['date']; + // $campo6 = '🛠️ ' . $jobInstallerNotificationData['jobDescription']; + // $campo7 = '📌 ' . $jobInstallerNotificationData['gmapsLocation']; + // $campo8 = '🔐 ' . $passwordAntenaCliente; + } + + if ($reprogramming && !$changeInstaller) { + $jobId = sprintf("#️⃣ *%s*", $jobInstallerNotificationData['jobId']); + $clientFullName = sprintf("👤 *%s*", $jobInstallerNotificationData['clientFullName']); + $date = sprintf("🗓️➡️ %s", $jobInstallerNotificationData['date']); + $installerName = sprintf("👷🏻‍♂️ *%s*", $jobInstallerNotificationData['installerName']); + + $campo1_combinado = "$installerName se reprogramó una tarea con el folio $jobId para el cliente $clientFullName, la nueva fecha será el $date"; + $campo2 = $jobInstallerNotificationData['clientWhatsApp']; + $campo3 = $jobInstallerNotificationData['gmapsLocation']; + $campo4 = $jobInstallerNotificationData['passwordAntenaCliente']; + + //Enviar notificación de reprogramación + $log->appendLog("Enviando notificación de reprogramación al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); + $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"88eeb6420a214fd8870dd28d741021c4\",\n \"optin_contact\": true\n }"; + } else if (!$reprogramming && $changeInstaller) { + //Enviar notificación de cambio de instalador + $log->appendLog("Enviando notificación de cambio de instalador al instalador anterior (desasignación), valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); + $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"e1aa2b0fd3884595918f4ac2676acd29\",\n \"optin_contact\": true\n }"; + } else if ($reprogramming && $changeInstaller) { + //Enviar notificación de cambio de instalador + $log->appendLog("Enviando notificación de cambio de instalador al instalador y notificación de reprogramación al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); + $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"e1aa2b0fd3884595918f4ac2676acd29\",\n \"optin_contact\": true\n }"; + } else if (!$reprogramming && !$changeInstaller) { + + //combinar el campo3, campo4, campo5, campo6, campo7 y campo8 en un solo campo con saltos de línea + $jobId = sprintf("#️⃣ *%s*", $jobInstallerNotificationData['jobId']); + $clientFullName = sprintf("👤 *%s*", $jobInstallerNotificationData['clientFullName']); + $date = sprintf("🗓️ %s", $jobInstallerNotificationData['date']); + $installerName = sprintf("👷🏻‍♂️ *%s*", $jobInstallerNotificationData['installerName']); + + $campo1_combinado = "$installerName se te ha asignado una tarea con folio $jobId, del cliente $clientFullName, para el $date"; + $campo2 = $jobInstallerNotificationData['clientWhatsApp']; + $campo3 = $jobInstallerNotificationData['gmapsLocation']; + $campo4 = $jobInstallerNotificationData['passwordAntenaCliente']; + + $log->appendLog("Enviando notificación normal de tarea al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); + $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"88eeb6420a214fd8870dd28d741021c4\",\n \"optin_contact\": true\n }"; + } + + $log->appendLog("La cadena CURL que se envia es: " . $curl_string); + curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); + + $response = curl_exec($ch); + $log->appendLog("Response del CallBell: " . $response); + + curl_close($ch); + + + + + // Validar la respuesta de Callbell + $jsonResponse = json_decode($response, true); + + if (json_last_error() === JSON_ERROR_NONE) { + // Evaluar si contiene "message" con "status": "enqueued" + if (isset($jsonResponse['message']) && $jsonResponse['message']['status'] === 'enqueued') { + $log->appendLog("La notificación fue enviada correctamente con estado: enqueued" . PHP_EOL); + return true; + } + + // Evaluar si contiene "error" + if (isset($jsonResponse['error'])) { + $log->appendLog("Error al enviar la notificación: " . json_encode($jsonResponse['error']) . PHP_EOL); + return false; + } + } else { + // Manejo de error en caso de que la respuesta no sea un JSON válido + $log->appendLog("La respuesta no es un JSON válido." . PHP_EOL); + return false; + } + + // Valor de retorno predeterminado en caso de que ninguna condición se cumpla + $log->appendLog("Ruta no prevista en la función." . PHP_EOL); + return false; + } + + public function sendPaymentNotificationWhatsApp($clientWhatsAppNumber, $notificationData): bool + { + + $log = PluginLogManager::create(); //Initialize Logger + + $log->appendLog("Eviando comprobante de pago al cliente: " . $notificationData->clientData['id'] . ' con número: ' . $clientWhatsAppNumber . PHP_EOL); + + // URL base de la API + $baseUri = 'https://' . $this->IPServer . '/crm/api/v1.0/'; + // Token de autenticación + $token = $this->UCRMAPIToken; + //Path base del comprobante de pago + $pdf_payment_path = ''; + + $this->ucrmApi = UcrmApi::create(); + $payments = $this->ucrmApi->get( + 'payments/', + [ + 'clientId' => $notificationData->clientData['id'], + 'limit' => 1, + 'direction' => 'DESC', + + ] + ); + + $payment_id = $payments[0]['id']; + $payment_amount = '$' . $payments[0]['amount']; + //$saldo = '$' . $notificationData->clientData['accountBalance']; + $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); + + $accountBalance = $notificationData->clientData['accountBalance']; + $saldoTexto = ''; + + if ($accountBalance < 0) { + // Si el saldo es negativo, quitar el signo y poner "Pendientes" + $saldoTexto = '$' . abs($accountBalance) . ' pendientes'; + } elseif ($accountBalance > 0) { + // Si el saldo es positivo, poner "a favor" + $saldoTexto = '$' . $accountBalance . ' a favor'; + } else { + // Si el saldo es 0, poner "está al corriente" + $saldoTexto = '$' . $accountBalance . ' está al corriente'; + } + + // Configuración del cliente GuzzleHttp + $clientGuzzleHttp = new Client([ + 'base_uri' => $baseUri, + 'headers' => [ + 'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización + 'Accept' => 'application/pdf', // Indica que esperamos una respuesta en formato JSON + ], + 'verify' => false, + ]); + + try { + // Hacer la solicitud GET para obtener el PDF + $response = $clientGuzzleHttp->request('GET', "payments/$payment_id/pdf"); + + // Leer el contenido del PDF de la respuesta + $contenidoArchivo = $response->getBody()->getContents(); + + // Construir el nombre del archivo PDF basado en el cliente + $fileNameComprobante = 'Comprobante_' . str_replace(' ', '_', $nombre_cliente) . '.pdf'; + $rutaArchivo = __DIR__ . '/../../comprobantes/' . $fileNameComprobante; + + // Guardar el contenido del PDF en un archivo local + if (file_put_contents($rutaArchivo, $contenidoArchivo) !== false) { + $log->appendLog("El archivo PDF se ha descargado y guardado correctamente en: $rutaArchivo" . PHP_EOL); + } else { + $log->appendLog("Error al guardar el archivo PDF en la ruta: $rutaArchivo" . PHP_EOL); + return false; + } + + if (!file_exists($rutaArchivo)) { + $log->appendLog("El archivo PDF no existe: $rutaArchivo" . PHP_EOL); + return false; + } + + if (filesize($rutaArchivo) === 0) { + $log->appendLog("El archivo PDF está vacío: $rutaArchivo" . PHP_EOL); + return false; + } + + $log->appendLog("El archivo PDF es válido y tiene contenido: $rutaArchivo" . PHP_EOL); + $rutaImagen = __DIR__ . '/../../comprobantes/' . str_replace('.pdf', '.png', $fileNameComprobante); + } catch (\Exception $e) { + $log->appendLog("Error al manejar el comprobante de pago: " . $e->getMessage() . PHP_EOL); + return false; + } + + try { + $image = new Imagick(); + $image->setResolution(300, 300); + $image->readImage($rutaArchivo); // Aquí podría ocurrir un error + $image->setImageFormat('png'); + $rutaImagen = str_replace('.pdf', '.png', $rutaArchivo); + $image->writeImage($rutaImagen); + $image->destroy(); + + $log->appendLog("El archivo PNG se ha generado correctamente en: $rutaImagen" . PHP_EOL); + } catch (ImagickException $e) { + $log->appendLog("Error al convertir el PDF a PNG con Imagick: " . $e->getMessage() . PHP_EOL); + return false; + } + + $fileNameComprobanteImage = str_replace('.pdf', '.png', $fileNameComprobante); + $url_file = $this->UploadReceiptToWordpressByImageFileName($fileNameComprobanteImage); //Carga del comprobante PDF a Wordpress para su posterior envío + // $url_file = $this->UploadReceiptToWordpressByImageFileName($fileNameComprobante);//Carga del comprobante PDF a Wordpress para su posterior envío + + $log->appendLog("Se terminó de subir comprobante a wordpress " . PHP_EOL); + + //$log->appendLog("Entrando al metodo sendPaymentNotificationWhatsAp" . PHP_EOL); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldoTexto\"],\n \"template_uuid\": \"57ead79cebd14902921477922403093b\",\n \"optin_contact\": true\n }"; + $log->appendLog("La cadena CURL que se envia es: " . $curl_string); + curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); + + $response = curl_exec($ch); + $log->appendLog("Response del CallBell: " . $response); + + //Enviar notificación adicional si es transferencia + // if($transferNotify){ + // curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_uuid\": \"41783c020cd84881bc27cb9e7e1da348\",\n \"optin_contact\": true\n }"); + // $response2 = curl_exec($ch); + // $log->appendLog("Response del CallBell aviso adicional: " . $response2); + // } + + curl_close($ch); + $this->deleteFilesExceptLastHundred(); + //return $fileNameComprobante; + + // Validar la respuesta de Callbell + $jsonResponse = json_decode($response, true); + + if (json_last_error() === JSON_ERROR_NONE) { + // Evaluar si contiene "message" con "status": "enqueued" + if (isset($jsonResponse['message']) && $jsonResponse['message']['status'] === 'enqueued') { + $log->appendLog("La notificación fue enviada correctamente con estado: enqueued" . PHP_EOL); + return true; + } + + // Evaluar si contiene "error" + if (isset($jsonResponse['error'])) { + $log->appendLog("Error al enviar la notificación: " . json_encode($jsonResponse['error']) . PHP_EOL); + return false; + } + } else { + // Manejo de error en caso de que la respuesta no sea un JSON válido + $log->appendLog("La respuesta no es un JSON válido." . PHP_EOL); + return false; + } + + // Valor de retorno predeterminado en caso de que ninguna condición se cumpla + $log->appendLog("Ruta no prevista en la función." . PHP_EOL); + return false; + } + public function sendTextPaymentNotificationWhatsApp($clientWhatsAppNumber, $notificationData): bool + { + + $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); + $folio = $notificationData->paymentData['id']; + $client_id = $notificationData->clientData['id']; + $fecha_pago = ''; + $cantidad_pagada = $notificationData->paymentData['amount']; + $metodo_pago = ''; + //$invoice_id= null; + $total_factura = null; + $pago_factura = $notificationData->paymentData['amount']; + // $saldoTexto= null; + $creditoPorPago = $notificationData->paymentData['creditAmount']; + $texto_credito = null; + $creditoClienteBase = $notificationData->clientData['accountCredit']; + + switch ($notificationData->paymentData['methodId']) { + case '11721cdf-a498-48be-903e-daa67552e4f6': + $metodo_pago = 'Cheque 📄'; + break; + case '6efe0fa8-36b2-4dd1-b049-427bffc7d369': + $metodo_pago = 'Efectivo 💵'; + break; + case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb': + $metodo_pago = 'Transferencia bancaria 📱🏦'; + break; + case '78e84000-9b5b-44a4-8367-da43df86ce34': + $metodo_pago = 'PayPal 🌐💳'; + break; + case '6da98bb9-6df7-4c41-8608-5cdd7fde7d5d': + $metodo_pago = 'Tarjeta de crédito PayPal 💳'; + break; + case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae': + $metodo_pago = 'Tarjeta de crédito Stripe 💳'; + break; + case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290': + $metodo_pago = 'Suscripción de Stripe (tarjeta de crédito) 🌐💳'; + break; + case '939f7701-00b7-4676-9b1e-17afb268c8ba': + $metodo_pago = 'Suscripción de PayPal 🌐💳'; + break; + case '1c963e35-df24-444d-95d2-12592d5107e8': + $metodo_pago = 'MercadoPago 🌐💳'; + break; + case 'd8c1eae9-d41d-479f-aeaf-38497975d7b3': + $metodo_pago = 'Personalizado 📝💸'; + break; + default: + $metodo_pago = 'Desconocido, revisar metodos de pago no contemplados'; + break; + } + + $log = PluginLogManager::create(); //Initialize Logger + + $log->appendLog("Eviando comprobante de pago al cliente: " . $notificationData->clientData['id'] . ' con número: ' . $clientWhatsAppNumber . PHP_EOL); + + // URL base de la API + $baseUri = 'https://' . $this->IPServer . '/crm/api/v1.0/'; + // Token de autenticación + $token = $this->UCRMAPIToken; + //Path base del comprobante de pago + $pdf_payment_path = ''; + + $this->ucrmApi = UcrmApi::create(); + $payments = $this->ucrmApi->get( + 'payments/', + [ + 'clientId' => $notificationData->clientData['id'], + 'limit' => 1, + 'direction' => 'DESC', + + ] + ); + + //$datos_payment_array = json_decode($response->getBody()->getContents(), true); + $datos_paymentJsonText = json_encode($payments, true); + $log->appendLog("Datos traidos con payment api: " . $datos_paymentJsonText . PHP_EOL); + + // $log->appendLog("Check 1" . PHP_EOL); + $paymentDate = new DateTime($fecha_pago); + + // Restar 6 horas + $paymentDate->modify('-6 hours'); + + // Formatear la fecha como "d/m/Y g:ia" (día/mes/año hora:minutos am/pm) + $fecha_pago = $paymentDate->format('d/m/Y g:ia'); + + $accountBalance = $notificationData->clientData['accountBalance']; + $saldoTexto = ''; + + if ($accountBalance < 0) { + // Si el saldo es negativo, quitar el signo y poner "Pendientes" + $saldoTexto = '$' . abs($accountBalance) . ' pendientes'; + } elseif ($accountBalance > 0) { + // Si el saldo es positivo, poner "a favor" + $saldoTexto = '$' . $accountBalance . ' a favor'; + } else { + // Si el saldo es 0, poner "está al corriente" + $saldoTexto = '$' . $accountBalance . ' está al corriente'; + } + + $log->appendLog("Datos client data credit amount: " . $notificationData->paymentData['creditAmount'] . PHP_EOL); + + $cantidad_pagadaFormateada = '$' . number_format($cantidad_pagada, 2, ',', '.') . ' MXN'; + $creditoPorPagoFormateado = '$' . number_format($creditoPorPago, 2, ',', '.') . ' MXN'; + + if ($creditoClienteBase > 0 && empty($notificationData->paymentData['paymentCovers'])) { + + $texto_credito = "La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito"; + $log->appendLog("La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito" . PHP_EOL); + + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$folio\", \"$client_id\", \"$fecha_pago\", \"$cantidad_pagadaFormateada\", \"$metodo_pago\", \"$saldoTexto\", \"$texto_credito\"],\n \"template_uuid\": \"4ac9dc060cf746b6ad7f2e8acad355e0\",\n \"optin_contact\": true\n }"; + } else { + + if ($creditoPorPago > 0) { + $texto_credito = "La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito"; + $log->appendLog("La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito" . PHP_EOL); + } else { + $texto_credito = '_________________________'; + } + + // Configuración del cliente GuzzleHttp + $clientGuzzleHttp = new Client([ + 'base_uri' => $baseUri, + 'headers' => [ + 'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización + 'Accept' => 'application/pdf', // Indica que esperamos una respuesta en formato JSON + ], + 'verify' => false, + ]); + + $log->appendLog("Verificar paymentCovers " . PHP_EOL); + + $log->appendLog("payment covers" . json_encode($notificationData->paymentData['paymentCovers']) . PHP_EOL); + + if (!empty($notificationData->paymentData['paymentCovers'])) { + $log->appendLog('Datos del payment covers:' . PHP_EOL); + + $invoiceIds = ''; // Variable para almacenar los invoiceId + $amounts = ''; // Variable para almacenar los amounts formateados + + foreach ($notificationData->paymentData['paymentCovers'] as $paymentCover) { + $log->appendLog('Invoice ID pagado: ' . $paymentCover['invoiceId'] . ' de esta cantidad: ' . $paymentCover['amount'] . PHP_EOL); + + // Concatenar cada invoiceId seguido de una coma + $invoiceIds .= $paymentCover['invoiceId'] . ', '; + + // Formatear el amount con punto para miles y coma para decimales + $formattedAmount = number_format($paymentCover['amount'], 2, ',', '.'); + + // Concatenar el amount formateado seguido de una coma + $amounts .= $formattedAmount . ', '; + } + + // Eliminar la última coma y el espacio extra usando substr + $invoiceIds = substr($invoiceIds, 0, -2); // Elimina los últimos dos caracteres (coma y espacio) + $amounts = substr($amounts, 0, -2); // Elimina los últimos dos caracteres (coma y espacio) + + // Mostrar las cadenas finales + $log->appendLog('Todos los Invoice IDs: ' . $invoiceIds . PHP_EOL); + $log->appendLog('Todas las cantidades: ' . $amounts . PHP_EOL); + + try { + + // Hacer la solicitud GET + $response = $clientGuzzleHttp->request('GET', "invoices/$invoiceIds"); + + // Realizar la solicitud a la API y guardar el contenido en un archivo local + $responseInvoices = $response->getBody()->getContents(); + $responseInvoicesJSON = json_decode($responseInvoices, true); + $log->appendLog('Numero de factura: ' . $responseInvoicesJSON['number'] . PHP_EOL); + $log->appendLog('TOTAL de factura: ' . $responseInvoicesJSON['total'] . PHP_EOL); + $total_factura = $responseInvoicesJSON['total']; + } catch (\Exception $e) { + $log->appendLog("Error con un problema al obtener alguna factura del cliente: " . $e->getMessage() . PHP_EOL); + return false; + } + } else { + $log->appendLog("no hay datos en payment covers" . PHP_EOL); + $invoiceIds = $notificationData->paymentData['id']; + $amounts = $notificationData->paymentData['amount']; + $total_factura = 0; + } + //$cantidad_pagadaFormateada = '$' . number_format($cantidad_pagada, 2, ',', '.') . ' MXN'; + $log->appendLog("Se terminó de verificar payment covers" . PHP_EOL); + $total_facturaFormateada = '$' . number_format($total_factura, 2, ',', '.') . ' MXN'; + $pagoFacturaFormateado = '$' . $amounts . ' MXN'; + + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$folio\", \"$client_id\", \"$fecha_pago\", \"$cantidad_pagadaFormateada\", \"$metodo_pago\", \"$invoiceIds\", \"$total_facturaFormateada\", \"$pagoFacturaFormateado\", \"$saldoTexto\", \"$texto_credito\"],\n \"template_uuid\": \"c1396a6ad3cb4192916d4ac2bfb782a5\",\n \"optin_contact\": true\n }"; + } + + //$log->appendLog("Entrando al metodo sendPaymentNotificationWhatsAp" . PHP_EOL); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + + //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"6c0df98317b44f7b8666375a6cc8454c\",\n \"optin_contact\": true\n }"; + + // $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }"; + $log->appendLog("La cadena CURL que se envia es: " . $curl_string); + //curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); + curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); + $response = curl_exec($ch); + $log->appendLog("Response del CallBell: " . $response); + + //Enviar notificación adicional si es transferencia + // if($transferNotify){ + // curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_uuid\": \"41783c020cd84881bc27cb9e7e1da348\",\n \"optin_contact\": true\n }"); + // $response2 = curl_exec($ch); + // $log->appendLog("Response del CallBell aviso adicional: " . $response2); + // } + + curl_close($ch); + //$this->deleteFilesExceptLastFifty(); + //return $fileNameComprobante; + // Validar la respuesta de Callbell + $jsonResponse = json_decode($response, true); + + if (json_last_error() === JSON_ERROR_NONE) { + // Evaluar si contiene "message" con "status": "enqueued" + if (isset($jsonResponse['message']) && $jsonResponse['message']['status'] === 'enqueued') { + $log->appendLog("La notificación fue enviada correctamente con estado: enqueued" . PHP_EOL); + return true; + } + + // Evaluar si contiene "error" + if (isset($jsonResponse['error'])) { + $log->appendLog("Error al enviar la notificación: " . json_encode($jsonResponse['error']) . PHP_EOL); + return false; + } + } else { + // Manejo de error en caso de que la respuesta no sea un JSON válido + $log->appendLog("La respuesta no es un JSON válido." . PHP_EOL); + return false; + } + + // Valor de retorno predeterminado en caso de que ninguna condición se cumpla + $log->appendLog("Ruta no prevista en la función." . PHP_EOL); + return false; + } + + public function sendOverdueNotificationWhatsApp($clientWhatsAppNumber, $notificationData): void + { + + $log = PluginLogManager::create(); //Initialize Logger + + // URL base de la API + //$baseUri = 'https://' . $this->IPServer . '/crm/api/v1.0/'; + // Token de autenticación + //$token = $this->UCRMAPIToken; + //Path base del comprobante de pago + //$pdf_payment_path = ''; + + // $this->ucrmApi = UcrmApi::create(); + // $invoices = $this->ucrmApi->get( + // 'invoices/', + // [ + // 'clientId' => $notificationData->clientData['id'], + // 'limit' => 1, + // 'direction' => 'DESC' + + // ] + // ); + + //$datos_payment_array = json_decode($payments,true); + //$datos_invoices = json_encode($invoices); + + //$log->appendLog("Datos traidos con invoices api: " . $datos_invoices . PHP_EOL); + //$log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos: " . $notificationData->paymentData[0]['createdDate']. PHP_EOL); + // $log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos opcion 2: " . $payments[0]['createdDate'] . PHP_EOL); + + // $invoice_id = $invoices[0]['id']; + // $invoices_amount = '$' . $invoices[0]['amount']; + $saldo = '$' . $notificationData->clientData['accountBalance']; + $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); + + // Configuración del cliente GuzzleHttp + // $clientGuzzleHttp = new Client([ + // 'base_uri' => $baseUri, + // 'headers' => [ + // 'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización + // 'Accept' => 'application/pdf', // Indica que esperamos una respuesta en formato JSON + // ], + // 'verify' => false, + // ]); + + // if (empty($notificationData->clientData['contacts'][0]['email'])) { + // $log->appendLog("El cliente no tiene correo" . PHP_EOL); + // } else { + // // $log->appendLog("El cliente SI tiene correo y es: ".$notificationData->clientData['contacts'][0]['email'] . PHP_EOL); + // // Hacer la solicitud PATCH para enviar correo electronico con el comprobante de pago + // $response = $clientGuzzleHttp->request('PATCH', "invoices/$invoice_id/send"); + + // } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + + // Verificar si la ruta es válida + // if ($realPath = realpath('/data/ucrm/data/plugins/siip-whatsapp-notifications/src/Facade/Comprobante.pdf')) { + // $log->appendLog("La ruta es válida: $realPath" . PHP_EOL); + // $log->appendLog(var_export($realPath,true) . PHP_EOL); + + // } else { + // $log->appendLog("La ruta no es válida o no existe" . PHP_EOL); + // } + + $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"SIIP INTERNET\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\",\n },\n \"template_values\": [\"$nombre_cliente\", \"$saldo\"],\n \"template_uuid\": \"9e7024c0a61a4c49b382150d26888dc2\",\n \"optin_contact\": true\n }"; + //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"57ead79cebd14902921477922403093b\",\n \"optin_contact\": true\n }"; + //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"6c0df98317b44f7b8666375a6cc8454c\",\n \"optin_contact\": true\n }"; + + // $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }"; + $log->appendLog("La cadena CURL que se envia es: " . $curl_string); + curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); + + $response = curl_exec($ch); + $log->appendLog("Response del CallBell: " . $response); + + curl_close($ch); + } + + public function getContactWhatsapp($cellphone_number): string + { + // URL de la API REST + $apiUrl = 'https://api.callbell.eu/v1/contacts/phone/' . $cellphone_number; + + // Inicializar cURL + $ch = curl_init(); + + // Configurar las opciones de cURL + curl_setopt($ch, CURLOPT_URL, $apiUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + + // Ejecutar la solicitud y obtener la respuesta + $response = curl_exec($ch); + + // Verificar si hay errores en la solicitud cURL + if (curl_errno($ch)) { + return 'Error al realizar la solicitud cURL: ' . curl_error($ch); + } else { + // Cerrar la sesión cURL + curl_close($ch); + return $response; + } + } + + public function patchWhatsapp($response_getContactCallBell, $notificationData, $clabeInterbancaria = null) + { + + $log = PluginLogManager::create(); //Initialize Logger + //imprimir notificacionData + $log->appendLog("Notificacion data: " . json_encode($notificationData) . PHP_EOL); + //ejemplo de $notificationData: {"uuid":"cad4fa25-176e-4823-9f4c-1421d05d2a31","changeType":"edit","entity":"client","entityId":2,"message":null,"clientId":2,"eventName":"client.edit","clientData":{"id":2,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"31 Chiapas","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"Chiapas 31, Centro, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":"Cliente espacial, el m\u00e1s chido","sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2024-01-25T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":true,"firstName":"Daniel Humberto","lastName":"Soto Villegas Pollerias2","username":"danydhsv","contacts":[{"id":2,"clientId":2,"email":"dhsv.141089@gmail.com","phone":"5214181878106","name":"Personal","isBilling":false,"isContact":false,"types":[{"id":1000,"name":"WhatsApp"}]},{"id":170,"clientId":2,"email":null,"phone":"5214181817609","name":"Bussiness","isBilling":false,"isContact":false,"types":[]}],"attributes":[{"id":112,"clientId":2,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_PetN1dhr4rx0kX","clientZoneVisible":true},{"id":113,"clientId":2,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"0021804341999569810","clientZoneVisible":true},{"id":178,"clientId":2,"customAttributeId":15,"name":"Site","key":"site","value":"0LOCS","clientZoneVisible":false},{"id":179,"clientId":2,"customAttributeId":16,"name":"Antena\/Sectorial","key":"antenaSectorial","value":"Sectorial-4b 172.16.13.16\/24","clientZoneVisible":false}],"accountBalance":1553.33,"accountCredit":1553.33,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[{"id":2,"name":"NS EXENTO","colorBackground":"#42a3df","colorText":"#fff"}],"invitationEmailSentDate":null,"avatarColor":"#f1df43","addressGpsLat":21.1564209,"addressGpsLon":-100.9384185,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":true,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"serviceData":null,"invoiceData":null,"paymentData":null} + + //buscar el attribute con el key site y antenaSectorial + $attributes = $notificationData->clientData['attributes']; //Obtener los atributos del cliente + $site = ''; + $antenaSectorial = ''; + + // Iterar sobre los atributos + foreach ($attributes as $attribute) { + // Verificar si el key es "site" + if ($attribute['key'] === 'site') { + $site = $attribute['value']; + } + // Verificar si el key es "antenaSectorial" + if ($attribute['key'] === 'antenaSectorial') { + $antenaSectorial = $attribute['value']; + } + } + + $log->appendLog("Dentro del proceso del patch: " . PHP_EOL); + $this->ucrmApi = UcrmApi::create(); + $payments = $this->ucrmApi->get( + 'payments/', + [ + 'clientId' => $notificationData->clientData['id'], + 'limit' => 1, + 'direction' => 'DESC', + + ] + ); + + //$datos_payment_array = json_decode($payments,true); + $datos_payment = json_encode($payments); + + $log->appendLog("Datos traidos con payment api: " . $datos_payment . PHP_EOL); + //$log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos: " . $notificationData->paymentData[0]['createdDate']. PHP_EOL); + // $log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos opcion 2: " . $payments[0]['createdDate'] . PHP_EOL); + + $uuid = $response_getContactCallBell['contact']['uuid']; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + + $ch2 = curl_init(); + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch2, CURLOPT_CUSTOMREQUEST, 'PATCH'); + curl_setopt($ch2, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + $UrlChatCallBell = 'https://api.callbell.eu/v1/contacts/' . $uuid; + curl_setopt($ch, CURLOPT_URL, $UrlChatCallBell); + curl_setopt($ch2, CURLOPT_URL, $UrlChatCallBell); + $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); + $log->appendLog("Nombre del cliente que se va a actualizar: " . $nombre_cliente . PHP_EOL); + $log->appendLog("UUID: " . $uuid . PHP_EOL); + + $payment_method_id = $payments[0]['methodId']; + $payment_method = ''; + + switch ($payment_method_id) { + case '11721cdf-a498-48be-903e-daa67552e4f6': + $payment_method = 'Cheque 📄'; + break; + case '6efe0fa8-36b2-4dd1-b049-427bffc7d369': + $payment_method = 'Efectivo 💵'; + break; + case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb': + $payment_method = 'Transferencia bancaria 📱🏦'; + break; + case '78e84000-9b5b-44a4-8367-da43df86ce34': + $payment_method = 'PayPal 🌐💳'; + break; + case '6da98bb9-6df7-4c41-8608-5cdd7fde7d5d': + $payment_method = 'Tarjeta de crédito PayPal 💳'; + break; + case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae': + $payment_method = 'Tarjeta de crédito Stripe 💳'; + break; + case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290': + $payment_method = 'Suscripción de Stripe (tarjeta de crédito) 🌐💳'; + break; + case '939f7701-00b7-4676-9b1e-17afb268c8ba': + $payment_method = 'Suscripción de PayPal 🌐💳'; + break; + case '1c963e35-df24-444d-95d2-12592d5107e8': + $payment_method = 'MercadoPago 🌐💳'; + break; + case 'd8c1eae9-d41d-479f-aeaf-38497975d7b3': + $payment_method = 'Personalizado 📝💸'; + break; + default: + $payment_method = 'Desconocido, revisar metodos de pago no contemplados'; + break; + } + + $fecha_actual = new DateTime(); + $fecha_actual->modify('-6 hours'); + $fecha_actual_ajustada = $fecha_actual->format("d/m/Y H:i"); + + $fecha_ultimoPago = new DateTime($payments[0]['createdDate']); + //$fecha_ultimoPago = $fecha_ultimoPago->modify('-6 hours'); + $fecha_ultimoPago_ajustada = $fecha_ultimoPago->format("d/m/Y H:i"); + + //$log->appendLog("las dos fechas ajustadas : " . $fecha_actual_ajustada.' aqui la otra: '.$fecha_ultimoPago_ajustada. PHP_EOL); + + // $attributes = $notificationData->clientData['attributes']; //Obtener los atributos del cliente + + // // Variable para almacenar los valores de los atributos que comienzan con "clabe" + // $clabeInterbancaria = ''; + + // // Iterar sobre los atributoss + // foreach ($attributes as $attribute) { + // // Verificar si la "key" comienza con "clabe" + // if (strpos($attribute['key'], 'clabe') === 0) { + // // Agregar el valor al array $clabeValues + // $clabeInterbancaria = $attribute['value']; + // } + // } + + $accountBalance = $notificationData->clientData['accountBalance']; + $saldoTexto = ''; + + if ($accountBalance < 0) { + // Si el saldo es negativo, quitar el signo y poner "Pendientes" + $saldoTexto = '💲' . abs($accountBalance) . ' pendientes'; + } elseif ($accountBalance > 0) { + // Si el saldo es positivo, poner "a favor" + $saldoTexto = '💲' . $accountBalance . ' a favor'; + } else { + // Si el saldo es 0, poner "está al corriente" + $saldoTexto = '💲' . $accountBalance . ' está al corriente'; + } + + $resumenClienteJSON = '{' . + '"Cliente": "' . $notificationData->clientData['id'] . '",' . + '"Domicilio": "' . + //(($notificationData->clientData['fullAddress'] == null) ? 'Sin domicilio' : '' . $notificationData->clientData['fullAddress']) . '",' . + '"Nombre": "' . sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']) . '",' . + '"URL": "https://sistema.siip.mx/crm/client/' . $notificationData->clientId . '",' . + '"Saldo Actual": "' . $saldoTexto . '",' . + '"Monto Ultimo Pago": "$ ' . $payments[0]['amount'] . '",' . + '"Estado": "' . + (($notificationData->clientData['hasSuspendedService']) ? 'Suspendido' : 'Activo') . '",' . + '"Fecha Ultimo Pago": " ' . $fecha_ultimoPago_ajustada . ' con ' . $payment_method . '",' . + '"Fecha Ultima Actualizacion": "' . $fecha_actual_ajustada . '",' . + '"Clabe Interbancaria": "' . $clabeInterbancaria . '",' . + '"Site": "' . $site . '",' . + '"Antena/Sectorial": "' . $antenaSectorial . '"' . + '}'; + + $data_CRM = [ + //"uuid" => $json_responseAPI->contact->uuid, + "name" => sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']), + + "custom_fields" => [ + "Cliente" => $notificationData->clientData['id'], + "Domicilio" => ($notificationData->clientData['fullAddress'] == null) ? '📍❌ Sin domicilio' : '📍 ' . $notificationData->clientData['fullAddress'], + "Nombre" => sprintf("👤 %s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']), + "URL" => '🌐 https://sistema.siip.mx/crm/client/' . $notificationData->clientId, + "Saldo Actual" => $saldoTexto, + "Monto Ultimo Pago" => '💲 ' . $payments[0]['amount'], + "Estado" => ($notificationData->clientData['hasSuspendedService']) ? '🔴 Suspendido' : '🟢 Activo ', + "Resumen" => $resumenClienteJSON, + "Fecha Ultimo Pago" => '📆💸 ' . $fecha_ultimoPago_ajustada . ' con ' . $payment_method, + "Fecha Ultima Actualizacion" => '📆🔄️ ' . $fecha_actual_ajustada, + "Clabe Interbancaria" => $clabeInterbancaria, + "Site" => $site, + "Antena/Sectorial" => $antenaSectorial, + ], + ]; + + $log->appendLog("JSON con los datos a actualizar: " . json_encode($data_CRM) . PHP_EOL); + + $data_CRM2 = [ + "custom_fields" => [ + "Resumen" => $resumenClienteJSON, + ], + ]; + + $log->appendLog("JSON con los datos a actualizar del resumen: " . $resumenClienteJSON . PHP_EOL); + + if ( + $response_getContactCallBell['custom_fields']['Cliente'] != $data_CRM['custom_fields']['Cliente'] + || $response_getContactCallBell['custom_fields']['Domicilio'] != $data_CRM['custom_fields']['Domicilio'] + || $response_getContactCallBell['custom_fields']['Nombre'] != $data_CRM['custom_fields']['Nombre'] + || $response_getContactCallBell['custom_fields']['URL'] != $data_CRM['custom_fields']['URL'] + || $response_getContactCallBell['custom_fields']['Saldo'] != $data_CRM['custom_fields']['Saldo'] + || $response_getContactCallBell['custom_fields']['Estado'] != $data_CRM['custom_fields']['Estado'] + || $response_getContactCallBell['custom_fields']['Fecha Ultimo Pago'] != $data_CRM['custom_fields']['Fecha Ultimo Pago'] + || $response_getContactCallBell['custom_fields']['Monto Ultimo Pago'] != $data_CRM['custom_fields']['Monto Ultimo Pago'] + || $response_getContactCallBell['name'] != $data_CRM['name'] + + ) { + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data_CRM)); + curl_setopt($ch2, CURLOPT_POSTFIELDS, json_encode($data_CRM2)); + $response = curl_exec($ch); + $log->appendLog("Response Patch CallBell: " . $response . PHP_EOL); + $response2 = curl_exec($ch2); + $log->appendLog("Response 2 Patch CallBell: " . $response2 . PHP_EOL); + curl_close($ch); + curl_close($ch2); + + // if($fileNameComprobante != null){ + // sleep(3); + // $this->deleteFileWordPressAndLocal($fileNameComprobante); + // } + + // $json_data_patch = '{ + // "attributes": [ + // { + // "value": "' . $UrlChatCallBell . '", + // "customAttributeId": 21 + // } + // ] + + // }'; //JSON para hacer patch de los custom fields del cliente en el UISCP CRM, Campo para el Stripe Customer ID y la Clabe interbancaria + + // $clientguzz = new Client(); //instancia de cliente GuzzleHttp para consumir API UISP CRM + // try { + // $responseCRM = $clientguzz->patch($baseUri . 'clients/' . $notificationData->clientData['id'], [ + // 'json' => json_decode($json_data_patch, true), + // 'headers' => [ + // 'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización + // 'Accept' => 'application/json', // Indica que esperamos una respuesta en formato JSON + // ], + // 'verify' => false, + // ]); //aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM + + // } catch (GuzzleException $error) { + // echo "Error al hacer el patch al CRM: " . $error->getMessage() . PHP_EOL; + // //exit(); + // } + // $log->appendLog(json_encode($responseCRM) . PHP_EOL); //imprimir respuesta del patch de CRM con la clabe y Customer ID Stripe + + } else { + $log->appendLog("No hay cambios que actualizar " . PHP_EOL); + } + } + + public function patchServiceStatusWhatsApp($response_getContactCallBell, $notificationData) + { + + $log = PluginLogManager::create(); //Initialize Logger + + $log->appendLog("Actualizando estado del servicio " . PHP_EOL); + + $uuid = $response_getContactCallBell['contact']['uuid']; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $this->CallBellAPIToken, + 'Content-Type: application/json', + ]); + $UrlChatCallBell = 'https://api.callbell.eu/v1/contacts/' . $uuid; + curl_setopt($ch, CURLOPT_URL, $UrlChatCallBell); + $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); + $log->appendLog("Nombre del cliente al que se va a actualizar el estado del servicio: " . $nombre_cliente . PHP_EOL); + $log->appendLog("UUID: " . $uuid . PHP_EOL); + + $fecha_actual = new DateTime(); + $fecha_actual->modify('-6 hours'); + $fecha_actual_ajustada = $fecha_actual->format("d/m/Y H:i"); + + $data_CRM = [ + "name" => $nombre_cliente, + "custom_fields" => [ + "Estado" => ( + isset($notificationData->serviceData['status']) + && $notificationData->serviceData['status'] == 3 + ) ? '🟠 Suspendido' : ( + (isset($notificationData->serviceData['status']) + && $notificationData->serviceData['status'] == 1) + ? '🟢 Activo' : '🚫 Finalizado'), + "Fecha Ultima Actualizacion" => '📆🔄️ ' . $fecha_actual_ajustada, + ], + ]; + + $log->appendLog("JSON con los datos a actualizar: " . json_encode($data_CRM) . PHP_EOL); + + if ($response_getContactCallBell['custom_fields']['Estado'] != $data_CRM['custom_fields']['Estado']) { + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data_CRM)); + $response = curl_exec($ch); + $log->appendLog("Response Patch CallBell: " . $response . PHP_EOL); + curl_close($ch); + } else { + $log->appendLog("No hay cambios en el estado del servicio que actualizar " . PHP_EOL); + } + } + + function UploadReceiptToWordpressByImageFileName($imageFileName): string + { + + $log = PluginLogManager::create(); //Initialize Logger + $configManager = PluginConfigManager::create(); + $config = $configManager->loadConfig(); + + // Configuración de conexión FTP + $ftp_server = $config['hostServerFTP']; + $ftp_username = $config['usernameServerFTP']; + $ftp_password = $config['passServerFTP']; + $remote_folder = "/public_html/wp/wp-content/uploads/img/"; + + $log->appendLog("Subiendo comprobante a worpdpress " . PHP_EOL); + // Configuración de conexión FTP + // $ftp_server = "siip.mx"; + // $ftp_username = "siip0001"; + // $ftp_password = '$spGiT,[wa)n'; + $remote_file = "/public_html/wp/wp-content/uploads/img/" . $imageFileName; + $file_to_upload = __DIR__ . '/../../comprobantes/' . $imageFileName; + $url = 'https://siip.mx/wp/wp-content/uploads/img/' . $imageFileName; + + $log->appendLog("file_to_upload: " . $file_to_upload . PHP_EOL); + + // Conexión FTP + $ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor FTP"); + $login = ftp_login($ftp_conn, $ftp_username, $ftp_password); + ftp_pasv($ftp_conn, true); + // Verificar conexión y login + if ($ftp_conn && $login) { + $log->appendLog("Conexión FTP exitosa" . PHP_EOL); + + // Cargar archivo + if (ftp_put($ftp_conn, $remote_file, $file_to_upload, FTP_BINARY)) { + $log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL); + $log->appendLog("La URL es: " . $url . PHP_EOL); + + $this->deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_file); + // Cerrar conexión FTP + ftp_close($ftp_conn); //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO + return $url; //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO + + } else { + $log->appendLog("Error al cargar el archivo " . PHP_EOL); + ftp_close($ftp_conn); + return ''; + } + + //SI SE DECIDE VOLVER A ELIMINAR LOS COMPROBANTES ENTONCES DESCOMENTAR ESTA PARTE DE ABAJO Y COMENTAR LA SECCIÓN DE ARRIBA + + // Obtener lista de archivos en la carpeta + // $files = ftp_nlist($ftp_conn, $remote_folder); + // if (is_array($files)) { + // // Eliminar la ruta del directorio de los archivos + // $files = array_map(function ($file) use ($remote_folder) { + // return str_replace($remote_folder, '', $file); + // }, $files); + + // // Obtener fechas de modificación + // $filesWithTime = []; + // foreach ($files as $file) { + // $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); + // if ($modifiedTime != -1) { + // $filesWithTime[$file] = $modifiedTime; + // } + // } + + // // Ordenar archivos por fecha de modificación, más recientes primero + // arsort($filesWithTime); + + // // Obtener los archivos a eliminar (todos menos los 50 más recientes) + // $filesToDelete = array_slice(array_keys($filesWithTime), 50); + + // // Eliminar archivos antiguos + // foreach ($filesToDelete as $file) { + // if (ftp_delete($ftp_conn, $remote_folder . $file)) { + // $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); + // } else { + // $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL); + // } + // } + // $log->appendLog("Archivos eliminados" . PHP_EOL); + // ftp_close($ftp_conn); + // return $url; + // } else { + // $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); + // ftp_close($ftp_conn); + // return $url; + // } + + } else { + $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); + return ''; + } + } + + function deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_folder): bool + { + + // Obtener lista de archivos en la carpeta + $files = ftp_nlist($ftp_conn, $remote_folder); + if (is_array($files)) { + // Eliminar la ruta del directorio de los archivos + $files = array_map(function ($file) use ($remote_folder) { + return str_replace($remote_folder, '', $file); + }, $files); + + // Obtener fechas de modificación + $filesWithTime = []; + foreach ($files as $file) { + $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); + if ($modifiedTime != -1) { + $filesWithTime[$file] = $modifiedTime; + } + } + + // Ordenar archivos por fecha de modificación, más recientes primero + arsort($filesWithTime); + + // Obtener los archivos a eliminar (todos menos los 50 más recientes) + $filesToDelete = array_slice(array_keys($filesWithTime), 50); + + // Eliminar archivos antiguos + foreach ($filesToDelete as $file) { + if (ftp_delete($ftp_conn, $remote_folder . $file)) { + $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); + } else { + $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL); + } + } + $log->appendLog("Archivos eliminados" . PHP_EOL); + ftp_close($ftp_conn); + return true; + } else { + $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); + ftp_close($ftp_conn); + return false; + } + } + + /** + * /Elimina los archivos del directorio de comprobantes de pago a excepción de los últimos 100 para evitar errores de envío + * @return void + */ + function deleteFilesExceptLastHundred() + { + $log = PluginLogManager::create(); //Initialize Logger + $directory = __DIR__ . '/../../comprobantes/'; + // Verificar si el directorio existe + if (!is_dir($directory)) { + $log->appendLog('El directorio no existe.'); + die("El directorio no existe."); + } + + // Obtener la lista de archivos en el directorio + $files = glob($directory . '/*'); + + // Ordenar los archivos por fecha de modificación, de más reciente a más antiguo + usort($files, function ($a, $b) { + return filemtime($b) - filemtime($a); + }); + + // Verificar que haya más de 100 archivos + if (count($files) > 100) { + // Obtener los archivos que deben ser eliminados (todos menos los 100 más recientes) + $filesToDelete = array_slice($files, 100); + + // Eliminar los archivos + foreach ($filesToDelete as $file) { + if (is_file($file)) { + unlink($file); + $log->appendLog("Archivo eliminado: $file" . PHP_EOL); + } + } + } else { + $log->appendLog("Hay menos de 100 archivos en el directorio. No se eliminarán archivos." . PHP_EOL); + } + } +} diff --git a/archivos_webhook_destino/src/Facade/PluginNotifierFacade.php b/archivos_webhook_destino/src/Facade/PluginNotifierFacade.php new file mode 100755 index 0000000..22d259e --- /dev/null +++ b/archivos_webhook_destino/src/Facade/PluginNotifierFacade.php @@ -0,0 +1,58 @@ +pluginData = $optionsManager->load(); + } + + + /* + * Send WhatsApp through the CallBell API + */ + protected function sendWhatsApp( + string $message, + ): void { + $this->logger->debug('Enviando mensaje desde twilio notifier facade: '.$message); + + + } + + /* + * Phone number of sender - required by Twilio. In this plugin, we only load it from config. + */ + private function getSenderNumber(): string + { + return $this->pluginData->twilioSmsNumber; + } +} diff --git a/archivos_webhook_destino/src/Facade/PluginOxxoNotifierFacade.php b/archivos_webhook_destino/src/Facade/PluginOxxoNotifierFacade.php new file mode 100755 index 0000000..9f54026 --- /dev/null +++ b/archivos_webhook_destino/src/Facade/PluginOxxoNotifierFacade.php @@ -0,0 +1,58 @@ +pluginData = $optionsManager->load(); + } + + + /* + * Send WhatsApp through the CallBell API + */ + protected function sendWhatsApp( + string $message, + ): void { + $this->logger->debug('Enviando mensaje desde twilio notifier facade: '.$message); + + + } + + /* + * Phone number of sender - required by Twilio. In this plugin, we only load it from config. + */ + private function getSenderNumber(): string + { + return $this->pluginData->twilioSmsNumber; + } +} diff --git a/archivos_webhook_destino/src/Facade/TwilioNotifierFacade.php b/archivos_webhook_destino/src/Facade/TwilioNotifierFacade.php new file mode 100755 index 0000000..0c7abfe --- /dev/null +++ b/archivos_webhook_destino/src/Facade/TwilioNotifierFacade.php @@ -0,0 +1,93 @@ +pluginData = $optionsManager->load(); + } + + /* + * Get Twilio SMS API object (unless it's already initialized) + */ + public function getTwilioClient(): Client + { + if (!$this->twilioClient) { + $this->twilioClient = new Client( + $this->pluginData->token_callbell + ); + } + return $this->twilioClient; + } + /* + * Send WhatsApp through the CallBell API + */ + protected function sendWhatsApp( + string $message, + ): void { + $this->logger->debug('Enviando mensaje desde twilio notifier facade: '.$message); + + + } + /* + * Send message through the Twilio client + */ + protected function sendMessage( + NotificationData $notificationData, + string $clientSmsNumber, + string $messageBody + ): void { + $this->logger->debug(sprintf('Sending: %s', $messageBody)); + + $messageInstance = $this->getTwilioClient()->messages->create( + $clientSmsNumber, + [ + 'from' => $this->getSenderNumber(), + 'body' => $messageBody, + ] + ); + + $this->logger->debug((string) $messageInstance); + $this->logger->info(sprintf('Twilio status: %s, message id: %s', $messageInstance->status, $messageInstance->sid)); + if ($messageInstance->errorCode) { + $this->logger->warning(sprintf('Twilio error: %s %s', $messageInstance->errorCode, $messageInstance->errorMessage)); + } + } + + /* + * Phone number of sender - required by Twilio. In this plugin, we only load it from config. + */ + private function getSenderNumber(): string + { + return $this->pluginData->twilioSmsNumber; + } +} diff --git a/archivos_webhook_destino/src/Facade/pruebas_ucrm_api.php b/archivos_webhook_destino/src/Facade/pruebas_ucrm_api.php new file mode 100755 index 0000000..71d04a0 --- /dev/null +++ b/archivos_webhook_destino/src/Facade/pruebas_ucrm_api.php @@ -0,0 +1,5 @@ +logger = $logger; + $this->optionsManager = $optionsManager; + $this->pluginDataValidator = $pluginDataValidator; + $this->notifierFacade = $notifierFacade; + $this->pluginNotifierFacade = $pluginNotifierFacade; + $this->pluginOxxoNotifierFacade = $pluginOxxoNotifierFacade; + $this->notificationDataFactory = $notificationDataFactory; + } + + public function run(): void + { + // $hola = PHP_SAPI; + // $this->logger->info('valor de PHP_SAPI: ' . $hola); + if (PHP_SAPI === 'fpm-fcgi') { + // $this->logger->debug('Whatsapp over HTTP started'); + $this->processHttpRequest(); + // $this->logger->debug('HTTP request processing ended.'); + } elseif (PHP_SAPI === 'cli') { + // $this->logger->debug('Whatsapp over CLI started'); + $this->processCli(); + // $this->logger->debug('CLI process ended.'); + } else { + throw new \UnexpectedValueException('Unknown PHP_SAPI type: ' . PHP_SAPI); + } + + } + + private function processCli(): void + { + if ($this->pluginDataValidator->validate()) { + $this->logger->info('Validating config'); + $this->optionsManager->load(); + } + } + + private function processHttpRequest(): void + { + $pluginData = $this->optionsManager->load(); + if ($pluginData->logging_level) { + $this->logger->setLogLevelThreshold(LogLevel::DEBUG); + } + + $userInput = file_get_contents('php://input'); //se recibe el json del webhook + //imprimir el json del webhook + $this->logger->debug('valor del webhook: ' . $userInput . PHP_EOL); + + if (! $userInput) { + $this->logger->warning('no input'); + + return; + } + + $jsonData = @json_decode($userInput, true, 50); + + if (! isset($jsonData['uuid'])) { + $this->logger->info('No UUID found in the webhook data'); + //$this->logger->error('JSON error: ' . json_last_error_msg()); + + //return; + + // Maneja el evento del webhook externo + if ($jsonData) { + switch ($jsonData['type']) { + case 'customer_cash_balance_transaction.created': + if ($jsonData['data']['object']['type'] === 'funded') { + $this->logger->info('Evento de transferencia de un cliente recibido: ' . json_encode($jsonData) . PHP_EOL); + $this->pluginNotifierFacade->createPaymentIntent($jsonData); + + } + + if ($jsonData['data']['object']['type'] === 'applied_to_payment') { + $this->logger->info('Se aplicó el saldo en Stripe de un pago: ' . json_encode($jsonData) . PHP_EOL); + + }elseif ($jsonData['data']['object']['type'] === 'unapplied_from_payment'){ + //ejemplo de json para transferencia de dinero cancelada: {"id":"evt_1RlEGgEFY1WEUtgR6Bp2DzDP","object":"event","api_version":"2023-10-16","created":1752606717,"data":{"object":{"id":"ccsbtxn_1RlEGfEFY1WEUtgRv8jAUGmE","object":"customer_cash_balance_transaction","created":1752606717,"currency":"mxn","customer":"cus_PetN1dhr4rx0kX","ending_balance":18000,"livemode":false,"net_amount":18000,"type":"unapplied_from_payment","unapplied_from_payment":{"payment_intent":"pi_3RlDPdEFY1WEUtgR1JBgNhTQ"}}},"livemode":false,"pending_webhooks":2,"request":{"id":"req_954mskVBfAI0jn","idempotency_key":"749518f6-baa0-4ae9-99e4-8029a35719aa"},"type":"customer_cash_balance_transaction.created"} + $paymentIntentId = $jsonData['data']['object']['unapplied_from_payment']['payment_intent']; + //Se canceló una transferencia de dinero, imprimir que se canceló y además el monto neto + $this->logger->warning('Evento de transferencia cancelada para el pago: ' . $paymentIntentId . PHP_EOL); + $this->logger->warning('Monto neto de la transferencia cancelada: ' . $jsonData['data']['object']['net_amount'] . PHP_EOL); + + } + break; + case 'payout.failed': + $this->logger->info('Evento de transferencia fallida encontrado: ' . json_encode($jsonData) . PHP_EOL); + //imprimir detalles del fallo + $this->logger->info('Detalles del fallo: ' . json_encode($jsonData)); + break; + case 'payment_intent.partially_funded': + $this->logger->info('Evento de pago parcialmente financiado encontrado: ' . json_encode($jsonData) . PHP_EOL); + //imprimir detalles del evento o pago + $this->logger->info('Detalles del evento: ' . json_encode($jsonData)); + break; + case 'inbound_payment.payment_attempt': + //$this->logger->info('Evento de Pago de OXXO recibido: ' . json_encode($jsonData) . PHP_EOL); + break; + case 'cash_balance.funds_available': + $this->logger->info('Evento de Pago de fondos disponibles recibido: ' . json_encode($jsonData) . PHP_EOL); + break; + case 'energy.alert': + $this->logger->info('Evento de Energía recibido: ' . $jsonData['message'] . PHP_EOL); + break; + case 'oxxo.request': + $this->logger->info('Evento de solicitud de referencia de oxxo recibido' . PHP_EOL); + + // Construir la URL basada en el "client_id" + // $url = "https://siip.mx/wp/wp-content/uploads/img/voucher.png"; + if (! empty($jsonData['amount'])) { + $this->logger->info('Referencia personalizada, Valor del monto: ' . $jsonData['amount'] . PHP_EOL); + $intentos = 0; + do { + // if ($intentos > 1) { + // sleep(2); + // } + $responseOxxo = $this->pluginOxxoNotifierFacade->createOxxoPaymentIntent($jsonData, $jsonData['amount']); + $this->logger->info('Referencia personalizada, Valor de la respuesta: ' . json_encode($responseOxxo) . PHP_EOL); + //El array asociativo $responseOxxo es un array asosiativo con los siguientes campos: oxxo_reference, error, failDescription, clientID, amount + $intentos++; + } while (strpos($responseOxxo['url'], 'https') !== 0 && $intentos < 3); //Mientras la url no contenga https y el número de intentos sea menor a 3 + } else { + $intentos = 0; + do { + // if ($intentos > 1) { + // sleep(2); + // } + $responseOxxo = $this->pluginOxxoNotifierFacade->createOxxoPaymentIntent($jsonData); + //El array asociativo $responseOxxo es un array asosiativo con los siguientes campos: oxxo_reference, error, failDescription, clientID, amount + $intentos++; + } while (strpos($responseOxxo['url'], 'https') !== 0 && $intentos < 3); //Mientras la url no contenga https y el número de intentos sea menor a 3 + } + + //El array asociativo $responseOxxo es un array asosiativo con los siguientes campos: oxxo_reference, error, failDescription, clientID, amount + //Si la respuesta no contiene https, se genera un error + if (strpos($responseOxxo['url'], 'https') !== 0) { + //this->logger->error('Error al crear la referencia de OXXO: ' . $responseOxxo); + //crear un json con variable $response que contenga las claves y valores del array $responseOxxo los cuales son: oxxo_reference, error, failDescription, clientID, clientFullName amount + $response = '{' . + '"url": "' . $responseOxxo['url'] . '",' . + '"oxxo_reference": "' . $responseOxxo['oxxo_reference'] . '",' . + '"voucher_image_url": "' . $responseOxxo['voucher_image_url'] . '",' . + '"error": "' . $responseOxxo['error'] . '",' . + '"failDescription": "' . $responseOxxo['failDescription'] . '",' . + '"clientID": "' . $responseOxxo['clientID'] . '",' . + '"clientFullName": "' . $responseOxxo['clientFullName'] . '",' . + '"amount": "' . $responseOxxo['amount'] . '"' . + '}'; + + header('Content-Type: application/json'); + echo $response; + break; + } else { + //crear un json con variable $response que contenga las claves y valores del array $responseOxxo los cuales son: oxxo_reference, error, failDescription, clientID, clientFullName, amount + + $response = '{' . + '"url": "' . $responseOxxo['url'] . '",' . + '"oxxo_reference": "' . $responseOxxo['oxxo_reference'] . '",' . + '"voucher_image_url": "' . $responseOxxo['voucher_image_url'] . '",' . + '"error": "' . $responseOxxo['error'] . '",' . + '"failDescription": "' . $responseOxxo['failDescription'] . '",' . + '"clientID": "' . $responseOxxo['clientID'] . '",' . + '"clientFullName": "' . $responseOxxo['clientFullName'] . '",' . + '"amount": "' . $responseOxxo['amount'] . '"' . + '}'; + + $this->logger->debug('Reponse que se envía a CallBell: ' . $response); + // Enviar el encabezado de respuesta como JSON + header('Content-Type: application/json'); + + // Enviar la respuesta en formato JSON + //echo json_encode($response); + echo $response; + + break; + + } + + } + } + return; + } + + // $event_json = json_decode($userInput); + // $webhook_string = json_encode($event_json); + // $this->logger->debug("El valor de webhook_string: " . $webhook_string . PHP_EOL); + + $notification = $this->notificationDataFactory->getObject($jsonData); + $this->logger->debug('valor el evento recibido por webhook: ' . $notification->eventName . PHP_EOL); + $this->logger->debug('Valor de JSON: ' . json_encode($jsonData) . PHP_EOL); + + if ($notification->changeType === 'test') { + + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + + $this->logger->info('Webhook test successful.'); + + return; + } else if ($notification->changeType === 'paperless.update') { + //imprimir el webhook json + $this->logger->info('Webhook de paperless update: ' . json_encode($jsonData) . PHP_EOL); + } + // if (!$notification->clientId) { + // $this->logger->warning('No client specified, cannot notify them.'); + + // return; + // } + + $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); + $config = $configManager->loadConfig(); + // the "exportFormat" key must be defined in plugin's manifest file, see the link above + + try { + + if ($notification->eventName === 'payment.add') { + + $result = json_encode($notification); + $this->logger->debug('Notification encodificado en JSON:' . $result . PHP_EOL); + + $datos_payment = $notification->paymentData; + $this->logger->debug('valor del payment data: ' . json_encode($datos_payment) . PHP_EOL); + $payment_method_id = $notification->paymentData['methodId']; + //$this->logger->debug('Metodo de pago: ' . $notification->paymentData['methodId'] . PHP_EOL); + + $payment_method = ''; + + switch ($payment_method_id) { + + case '11721cdf-a498-48be-903e-daa67552e4f6': + $payment_method = 'Cheque'; + if ($config['checkPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case '6efe0fa8-36b2-4dd1-b049-427bffc7d369': + $payment_method = 'Efectivo'; + if ($config['cashPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb': + $payment_method = 'Transferencia bancaria'; + if ($config['bankTransferPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case '78e84000-9b5b-44a4-8367-da43df86ce34': + $payment_method = 'PayPal'; + if ($config['paypalPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case '6da98bb9-6df7-4c41-8608-5cdd7fde7d5d': + $payment_method = 'Tarjeta de crédito PayPal'; + if ($config['creditCardPaypalPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae': + $payment_method = 'Tarjeta de crédito Stripe'; + if ($config['creditCardStripePaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290': + $payment_method = 'Suscripción de Stripe (tarjeta de crédito)'; + if ($config['stripeSubscriptionCreditCardPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case '939f7701-00b7-4676-9b1e-17afb268c8ba': + $payment_method = 'Suscripción de PayPal'; + if ($config['paypalSubscriptionPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case '1c963e35-df24-444d-95d2-12592d5107e8': + $payment_method = 'MercadoPago'; + if ($config['mercadopagoPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case 'd8c1eae9-d41d-479f-aeaf-38497975d7b3': + $payment_method = 'Personalizado'; + if ($config['customPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + case '72271b72-5c0a-45e2-94d1-cdf4d7cf10e2': + $payment_method = 'Cortesía'; + if ($config['courtesyPaymentMethodId']) { + $this->notifierFacade->verifyPaymentActionToDo($notification); + } + break; + default: + $payment_method = 'Desconocido, revisar metodos de pago no contemplados'; + break; + + } + + } else if ($notification->eventName === 'client.edit') { + $this->logger->debug('Se actualiza a un cliente'); + $this->logger->debug('Valor de json_data: ' . json_encode($jsonData)); + //ejemplo de json_data: {"uuid":"aacaf5c5-2bf4-44ea-864f-a24121b453bb","changeType":"edit","entity":"client","entityId":"171","eventName":"client.edit","extraData":{"entity":{"id":171,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-05-21T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Archi","lastName":"Isalas","username":"mainstreamm2@gmail.com","contacts":[{"id":177,"clientId":171,"email":"mainstreamm2@gmail.com","phone":"4181878106","name":null,"isBilling":false,"isContact":false,"types":[]}],"attributes":[{"id":198,"clientId":171,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_SM2zH6IsjTz6ol","clientZoneVisible":true},{"id":199,"clientId":171,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"124180950530868794","clientZoneVisible":true}],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#f1df43","addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":true,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"entityBeforeEdit":{"id":171,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-05-21T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Archi","lastName":"Isalas","username":"mainstreamm2@gmail.com","contacts":[{"id":177,"clientId":171,"email":"mainstreamm2@gmail.com","phone":"4181878106","name":null,"isBilling":false,"isContact":false,"types":[{"id":1003,"name":"WhatsNotifica"}]}],"attributes":[{"id":198,"clientId":171,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_SM2zH6IsjTz6ol","clientZoneVisible":true},{"id":199,"clientId":171,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"124180950530868794","clientZoneVisible":true}],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#f1df43","addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":true,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false}}} + + + $clientID = $jsonData['entityId']; + $this->ucrmApi = UcrmApi::create(); + $customAttributes = $this->ucrmApi->get('custom-attributes/', ['attributeType' => 'client']); //Obtener los atributos del sistema que estén vinculados a la entidad "cliente" + //$this->logger->info("result del custom Attributes: " . json_encode($customAttributes) . PHP_EOL); + + $idPasswordAntenaCliente = null; + $passwordAntenaValue = null; + + //ejemplo de $customAttributes: [{"id":1,"name":"ip","key":"ip","attributeType":"client","type":"string","clientZoneVisible":false},{"id":2,"name":"ubntpass","key":"ubntpass","attributeType":"client","type":"string","clientZoneVisible":false},{"id":3,"name":"adminpass","key":"adminpass","attributeType":"client","type":"string","clientZoneVisible":true},{"id":4,"name":"ssid","key":"ssid","attributeType":"client","type":"string","clientZoneVisible":true},{"id":5,"name":"clavessid","key":"clavessid","attributeType":"client","type":"string","clientZoneVisible":true},{"id":6,"name":"clave","key":"clave","attributeType":"client","type":"string","clientZoneVisible":true},{"id":11,"name":"latitud","key":"latitud","attributeType":"client","type":"string","clientZoneVisible":true},{"id":12,"name":"longitud","key":"longitud","attributeType":"client","type":"string","clientZoneVisible":true},{"id":16,"name":"instalador","key":"instalador","attributeType":"client","type":"string","clientZoneVisible":true},{"id":17,"name":"creado por","key":"creadoPor","attributeType":"client","type":"string","clientZoneVisible":true},{"id":21,"name":"Chat de CallBell","key":"chatDeCallbell","attributeType":"client","type":"string","clientZoneVisible":false},{"id":22,"name":"uuid","key":"uuid","attributeType":"client","type":"string","clientZoneVisible":false},{"id":23,"name":"zona","key":"zona","attributeType":"client","type":"enum","clientZoneVisible":true},{"id":29,"name":"Stripe Customer ID","key":"stripeCustomerId","attributeType":"client","type":"string","clientZoneVisible":true},{"id":30,"name":"Clabe Interbancaria","key":"clabeInterbancaria","attributeType":"client","type":"string","clientZoneVisible":true},{"id":31,"name":"RUTA DE COBRANZA","key":"rutaDeCobranza","attributeType":"client","type":"enum","clientZoneVisible":true},{"id":35,"name":"Site","key":"site","attributeType":"client","type":"string","clientZoneVisible":true},{"id":36,"name":"Antena/Sectorial","key":"antenaSectorial","attributeType":"client","type":"string","clientZoneVisible":true},{"id":37,"name":"Password Antena Cliente","key":"passwordAntenaCliente","attributeType":"client","type":"string","clientZoneVisible":false}] + + // Verificar si se obtuvieron los atributos + if ($customAttributes && is_array($customAttributes)) { + foreach ($customAttributes as $attribute) { + // Verificar si 'name' contiene la palabra 'passwordAntenaCliente' sin distinguir mayúsculas y minúsculas + if (isset($attribute['key']) && stripos($attribute['key'], 'passwordAntenaCliente') !== false) { + $this->logger->info("ID correspondiente a 'passwordAntenaCliente': " . $attribute['id'] . PHP_EOL); + $idPasswordAntenaCliente = $attribute['id']; + } + + } + } else { + $this->logger->info("Error al obtener los atributos personalizados." . PHP_EOL); + } + + //buscar en los attributes ($jsonData) del cliente el id del atributo personalizado 'passwordAntenaCliente' si no está o si está pero está en blanco se manda llamar la función $this->notifierFacade->getVaultCredentials($clientID); + + foreach ($jsonData['extraData']['entity']['attributes'] as $attribute) { + if ($attribute['customAttributeId'] === $idPasswordAntenaCliente) { + $this->logger->info("El valor de passwordAntenaValue es: " . $attribute['value'] . PHP_EOL); + $passwordAntenaValue = $attribute['value']; + } + } + + //si el value de passwordAntenaValue es igual a null o cadena vacía se manda llamar la función $this->notifierFacade->getVaultCredentials($clientID); + if ($passwordAntenaValue === null || $passwordAntenaValue === '') { + $password = $this->notifierFacade->getVaultCredentials($clientID); + $this->logger->info("El valor de passwordAntenaValue es null o cadena vacía, se manda llamar la función getVaultCredentials" . PHP_EOL); + $this->logger->info("El valor de password es: " . $password . PHP_EOL); + + if ($this->notifierFacade->patchClientCustomAttribute($clientID, $idPasswordAntenaCliente, $password)) { + $this->logger->info("Se actualizó el atributo personalizado passwordAntenaCliente con el valor: " . $password . PHP_EOL); + } else { + $this->logger->info("No se pudo actualizar el atributo personalizado passwordAntenaCliente" . PHP_EOL); + } + } else { + $this->logger->info("Ya existe un valor de passwordAntenaValue: " . $passwordAntenaValue . PHP_EOL); + } + + //ejemplo de json_data: {"uuid":"17e043a7-03b5-4312-ab81-a7818124a77e","changeType":"edit","entity":"client","entityId":"158","eventName":"client.edit","extraData":{"entity":{"id":158,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"23 San Luis","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37804","fullAddress":"San Luis 23, Guadalupe, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-01-06T00:00:00-0600","leadConvertedAt":"2025-02-09T03:15:49-0600","companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Luis","lastName":"Guti\u00e9rrez","username":null,"contacts":[{"id":162,"clientId":158,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#2196f3","addressGpsLat":21.153272,"addressGpsLon":-100.9134508,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"entityBeforeEdit":{"id":158,"userIdent":null,"previousIsp":null,"isLead":true,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"23 San Luis","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37804","fullAddress":"San Luis 23, Guadalupe, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-01-06T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Luis","lastName":"Guti\u00e9rrez","username":null,"contacts":[{"id":162,"clientId":158,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#2196f3","addressGpsLat":21.153272,"addressGpsLon":-100.9134508,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false}}} + + // Validar que 'extraData' existe y contiene las claves necesarias + if ( + isset($jsonData['extraData']['entityBeforeEdit']) && + isset($jsonData['extraData']['entity']) + ) { + $entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit']; + $entity = $jsonData['extraData']['entity']; + + //$this->logger->debug('Validando claves dentro de entityBeforeEdit y entity'); + + // Validar si 'isLead' esta en true en entityBeforeEdit y en false en entity + if (array_key_exists('isLead', $entityBeforeEdit) && array_key_exists('isLead', $entity)) { + //$this->logger->debug('Los datos entityBeforeEdit y entity contienen el campo isLead'); + + $isLeadBefore = $entityBeforeEdit['isLead']; + $isLeadAfter = $entity['isLead']; + + // Comprobar si 'isLead' cambió de true a false + if ($isLeadBefore === true && $isLeadAfter === false) { + $this->logger->debug('El cliente cambió de potencial a cliente'); + //$this->pluginNotifierFacade->createStripeClient($notification); //Se comenta esta línea para que no se cree el cliente en Stripe al convertir el lead a cliente, ya que se hará al agregar la etiqueta 'CREAR CLABE STRIPE' + } else { + $this->logger->debug('El cliente no cambió de potencial a cliente'); + } + } else { + $this->logger->warning('El campo isLead no existe en entityBeforeEdit o entity'); + } + + // buscar si existe la etiqueta 'STRIPE' en entity pero no en entityBeforeEdit + $tags = $jsonData['extraData']['entity']['tags']; + $tagsBefore = $jsonData['extraData']['entityBeforeEdit']['tags']; + + $this->logger->debug('Validando claves dentro de entity y entityBeforeEdit'); + + // Validar que 'tags' existe en ambas entidades + if (array_key_exists('tags', $jsonData['extraData']['entity']) && array_key_exists('tags', $jsonData['extraData']['entityBeforeEdit'])) { + $this->logger->debug('Los datos entity y entityBeforeEdit contienen el campo tags'); + + $tags = $jsonData['extraData']['entity']['tags']; + $tagsBefore = $jsonData['extraData']['entityBeforeEdit']['tags']; + + $this->logger->debug('Validando si la etiqueta STRIPE existe en entity pero no en entityBeforeEdit'); + + // Comprobar si la etiqueta 'CREAR CLABE STRIPE' existe en 'tags' pero no en 'tagsBefore' + $stripeTagExists = false; + $stripeTagExistsBefore = false; + foreach ($tags as $tag) { + if ($tag['name'] === 'CREAR CLABE STRIPE') { + $stripeTagExists = true; + break; + } + } + foreach ($tagsBefore as $tag) { + if ($tag['name'] === 'CREAR CLABE STRIPE') { + $stripeTagExistsBefore = true; + break; + } + } + + // Comprobar si la etiqueta 'STRIPE' existe en 'tags' pero no en 'tagsBefore' + if ($stripeTagExists && ! $stripeTagExistsBefore) { + $this->logger->debug('La etiqueta CREAR CLABE STRIPE se agregará al cliente'); + $this->pluginNotifierFacade->createStripeClient($notification, true); + } + } else { + $this->logger->warning('El campo tags no existe en entity o entityBeforeEdit'); + } + + } else { + $this->logger->warning('Los datos entityBeforeEdit o entity no están presentes en extraData'); + } + + $this->notifierFacade->verifyClientActionToDo($notification); + + } else if ($notification->eventName === 'client.add') { + $this->logger->debug('Se agregó un nuevo cliente' . PHP_EOL); + $this->logger->debug('Valor de json_data: ' . json_encode($jsonData)); + + // Verificar que existen tanto 'entityBeforeEdit' como 'entity' + // if (isset($jsonData['extraData']['entity']['isLead'])) { + // $this->logger->debug('El campo isLead existe en los datos del evento'); + // $isLead = $jsonData['extraData']['entity']['isLead']; + + // // Comprobar si 'isLead' es true + // if ($isLead === true) { + // $this->logger->debug('El cliente es potencial'); + // $this->pluginNotifierFacade->createStripeClient($notification); + // } else { + // $this->logger->debug('El cliente no es potencial'); + // $this->pluginNotifierFacade->createStripeClient($notification); + // } + // } else { + // $this->logger->warning('El campo isLead no existe en los datos del evento'); + // } + + } else if ($notification->eventName === 'service.edit') { + $this->logger->debug('Se editó el servicio a un cliente' . PHP_EOL); + $this->notifierFacade->verifyServiceActionToDo($notification); + //ejemplo de json_data: {"uuid":"06d281ca-d78e-4f0a-a282-3a6b77d25da0","changeType":"edit","entity":"service","entityId":"155","eventName":"service.edit","extraData":{"entity":{"id":155,"prepaid":false,"clientId":171,"status":1,"name":"Basico 300","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","note":null,"addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"servicePlanId":6,"servicePlanPeriodId":26,"price":300,"hasIndividualPrice":false,"totalPrice":300,"currencyCode":"MXN","invoiceLabel":null,"contractId":null,"contractLengthType":1,"minimumContractLengthMonths":null,"activeFrom":"2025-05-21T00:00:00-0600","activeTo":null,"contractEndDate":null,"discountType":0,"discountValue":null,"discountInvoiceLabel":"Descuento","discountFrom":null,"discountTo":null,"tax1Id":null,"tax2Id":null,"tax3Id":null,"invoicingStart":"2025-05-21T00:00:00-0600","invoicingPeriodType":1,"invoicingPeriodStartDay":1,"nextInvoicingDayAdjustment":10,"invoicingProratedSeparately":true,"invoicingSeparately":false,"sendEmailsAutomatically":null,"useCreditAutomatically":true,"servicePlanName":"Basico 300","servicePlanPrice":300,"servicePlanPeriod":1,"servicePlanType":"Internet","downloadSpeed":8,"uploadSpeed":8,"hasOutage":false,"unmsClientSiteStatus":null,"fccBlockId":null,"lastInvoicedDate":null,"unmsClientSiteId":"359cb58d-e64f-453a-890e-23d5abb4f116","attributes":[],"addressData":null,"suspensionReasonId":null,"serviceChangeRequestId":null,"setupFeePrice":null,"earlyTerminationFeePrice":null,"downloadSpeedOverride":null,"uploadSpeedOverride":null,"trafficShapingOverrideEnd":null,"trafficShapingOverrideEnabled":false,"servicePlanGroupId":null,"suspensionPeriods":[],"surcharges":[]},"entityBeforeEdit":{"id":155,"prepaid":false,"clientId":171,"status":1,"name":"Basico 300","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","note":null,"addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"servicePlanId":6,"servicePlanPeriodId":26,"price":300,"hasIndividualPrice":false,"totalPrice":300,"currencyCode":"MXN","invoiceLabel":null,"contractId":null,"contractLengthType":1,"minimumContractLengthMonths":null,"activeFrom":"2025-05-21T00:00:00-0600","activeTo":null,"contractEndDate":null,"discountType":0,"discountValue":null,"discountInvoiceLabel":"Descuento","discountFrom":null,"discountTo":null,"tax1Id":null,"tax2Id":null,"tax3Id":null,"invoicingStart":"2025-05-21T00:00:00-0600","invoicingPeriodType":1,"invoicingPeriodStartDay":1,"nextInvoicingDayAdjustment":10,"invoicingProratedSeparately":true,"invoicingSeparately":false,"sendEmailsAutomatically":null,"useCreditAutomatically":true,"servicePlanName":"Basico 300","servicePlanPrice":300,"servicePlanPeriod":1,"servicePlanType":"Internet","downloadSpeed":8,"uploadSpeed":8,"hasOutage":false,"unmsClientSiteStatus":null,"fccBlockId":null,"lastInvoicedDate":null,"unmsClientSiteId":"359cb58d-e64f-453a-890e-23d5abb4f116","attributes":[],"addressData":null,"suspensionReasonId":null,"serviceChangeRequestId":null,"setupFeePrice":null,"earlyTerminationFeePrice":null,"downloadSpeedOverride":null,"uploadSpeedOverride":null,"trafficShapingOverrideEnd":null,"trafficShapingOverrideEnabled":false,"servicePlanGroupId":null,"suspensionPeriods":[],"surcharges":[]}}} + //obtener el clientID y asginarlo a la variable $clientID + $clientID = $jsonData['extraData']['entity']['clientId']; + $this->ucrmApi = UcrmApi::create(); + $customAttributes = $this->ucrmApi->get('custom-attributes/', ['attributeType' => 'client']); //Obtener los atributos del sistema que estén vinculados a la entidad "cliente" + //$this->logger->info("result del custom Attributes: " . json_encode($customAttributes) . PHP_EOL); + + $idPasswordAntenaCliente = null; + $passwordAntenaValue = null; + + //ejemplo de $customAttributes: [{"id":1,"name":"ip","key":"ip","attributeType":"client","type":"string","clientZoneVisible":false},{"id":2,"name":"ubntpass","key":"ubntpass","attributeType":"client","type":"string","clientZoneVisible":false},{"id":3,"name":"adminpass","key":"adminpass","attributeType":"client","type":"string","clientZoneVisible":true},{"id":4,"name":"ssid","key":"ssid","attributeType":"client","type":"string","clientZoneVisible":true},{"id":5,"name":"clavessid","key":"clavessid","attributeType":"client","type":"string","clientZoneVisible":true},{"id":6,"name":"clave","key":"clave","attributeType":"client","type":"string","clientZoneVisible":true},{"id":11,"name":"latitud","key":"latitud","attributeType":"client","type":"string","clientZoneVisible":true},{"id":12,"name":"longitud","key":"longitud","attributeType":"client","type":"string","clientZoneVisible":true},{"id":16,"name":"instalador","key":"instalador","attributeType":"client","type":"string","clientZoneVisible":true},{"id":17,"name":"creado por","key":"creadoPor","attributeType":"client","type":"string","clientZoneVisible":true},{"id":21,"name":"Chat de CallBell","key":"chatDeCallbell","attributeType":"client","type":"string","clientZoneVisible":false},{"id":22,"name":"uuid","key":"uuid","attributeType":"client","type":"string","clientZoneVisible":false},{"id":23,"name":"zona","key":"zona","attributeType":"client","type":"enum","clientZoneVisible":true},{"id":29,"name":"Stripe Customer ID","key":"stripeCustomerId","attributeType":"client","type":"string","clientZoneVisible":true},{"id":30,"name":"Clabe Interbancaria","key":"clabeInterbancaria","attributeType":"client","type":"string","clientZoneVisible":true},{"id":31,"name":"RUTA DE COBRANZA","key":"rutaDeCobranza","attributeType":"client","type":"enum","clientZoneVisible":true},{"id":35,"name":"Site","key":"site","attributeType":"client","type":"string","clientZoneVisible":true},{"id":36,"name":"Antena/Sectorial","key":"antenaSectorial","attributeType":"client","type":"string","clientZoneVisible":true},{"id":37,"name":"Password Antena Cliente","key":"passwordAntenaCliente","attributeType":"client","type":"string","clientZoneVisible":false}] + + // Verificar si se obtuvieron los atributos + if ($customAttributes && is_array($customAttributes)) { + foreach ($customAttributes as $attribute) { + // Verificar si 'name' contiene la palabra 'passwordAntenaCliente' sin distinguir mayúsculas y minúsculas + if (isset($attribute['key']) && stripos($attribute['key'], 'passwordAntenaCliente') !== false) { + $this->logger->info("ID correspondiente a 'passwordAntenaCliente': " . $attribute['id'] . PHP_EOL); + $idPasswordAntenaCliente = $attribute['id']; + } + + } + } else { + $this->logger->info("Error al obtener los atributos personalizados." . PHP_EOL); + } + + //buscar en los attributes ($jsonData) del cliente el id del atributo personalizado 'passwordAntenaCliente' si no está o si está pero está en blanco se manda llamar la función $this->notifierFacade->getVaultCredentials($clientID); + + foreach ($jsonData['extraData']['entity']['attributes'] as $attribute) { + if ($attribute['customAttributeId'] === $idPasswordAntenaCliente) { + $this->logger->info("El valor de passwordAntenaValue es: " . $attribute['value'] . PHP_EOL); + $passwordAntenaValue = $attribute['value']; + } + } + + //si el value de passwordAntenaValue es igual a null o cadena vacía se manda llamar la función $this->notifierFacade->getVaultCredentials($clientID); + if ($passwordAntenaValue === null || $passwordAntenaValue === '') { + $password = $this->notifierFacade->getVaultCredentials($clientID); + $this->logger->info("El valor de passwordAntenaValue es null o cadena vacía, se manda llamar la función getVaultCredentials" . PHP_EOL); + $this->logger->info("El valor de password es: " . $password . PHP_EOL); + + if ($this->notifierFacade->patchClientCustomAttribute($clientID, $idPasswordAntenaCliente, $password)) { + $this->logger->info("Se actualizó el atributo personalizado passwordAntenaCliente con el valor: " . $password . PHP_EOL); + } else { + $this->logger->info("No se pudo actualizar el atributo personalizado passwordAntenaCliente" . PHP_EOL); + } + } else { + $this->logger->info("Ya existe un valor de passwordAntenaValue: " . $passwordAntenaValue . PHP_EOL); + } + + } else if ($notification->eventName === 'service.suspend') { + $this->logger->debug('Se suspendió el servicio a un cliente' . PHP_EOL); + $this->notifierFacade->verifyServiceActionToDo($notification); + } else if ($notification->eventName === 'service.suspend_cancel') { + $this->logger->debug('Se reactivó el servicio a un cliente' . PHP_EOL); + $this->notifierFacade->verifyServiceActionToDo($notification); + } else if ($notification->eventName === 'service.postpone') { + $this->logger->debug('Se pospuso la suspención del servicio a un cliente' . PHP_EOL); + $this->notifierFacade->verifyServiceActionToDo($notification); + } else if ($notification->eventName === 'invoice.near_due') { + $this->logger->debug('Factura casi por vencer' . PHP_EOL); + $this->notifierFacade->notifyOverDue($notification); + } else if ($notification->eventName === 'invoice.overdue') { + $this->logger->debug('Factura vencida' . PHP_EOL); + $result = json_encode($notification); + $this->logger->debug('datos del notification para el invoice overdue:' . $result . PHP_EOL); + $this->notifierFacade->notifyOverDue($notification); + } else if ($notification->eventName === 'invoice.add') { + $this->logger->debug('Adición de Factura' . PHP_EOL); + $result = json_encode($notification); + $this->logger->debug('datos del notification para el invoice add:' . $result . PHP_EOL); + + $accountBalance = $notification->clientData['accountBalance']; + //$invoiceAmountPaid = $notification->invoiceData['amountPaid']; + $this->logger->debug("Account Balance: " . $accountBalance . PHP_EOL); + // $this->logger->debug("Pago hecho con la factura: " . $invoiceAmountPaid . PHP_EOL); + $this->notifierFacade->verifyInvoiceActionToDo($notification); + } else if ($notification->eventName === 'invoice.edit') { + $this->logger->debug('Edición de Factura' . PHP_EOL); + $this->notifierFacade->verifyInvoiceActionToDo($notification); + } else if ($notification->eventName === 'invoice.add_draft') { + $this->logger->debug('Adición de borrador de Factura' . PHP_EOL); + $this->notifierFacade->verifyInvoiceActionToDo($notification); + } else if ($notification->eventName === 'invoice.draft_approved') { + $this->logger->debug('Aprobación de Factura' . PHP_EOL); + $this->notifierFacade->verifyInvoiceActionToDo($notification); + } else if ($notification->eventName === 'invoice.delete') { + $this->logger->debug('Eliminación de Factura' . PHP_EOL); + $this->notifierFacade->verifyInvoiceActionToDo($notification); + } else if ($notification->eventName === 'job.add') { + $this->logger->debug('Se ha agregado un nuevo trabajo' . PHP_EOL); + $this->logger->debug('Valor de json_data: ' . json_encode($jsonData)); + //Ejemplo de json_data: {"uuid":"434b3da0-984a-4358-a1b6-2a4418bacc49","changeType":"insert","entity":"job","entityId":"38","eventName":"job.add","extraData":{"entity":{"id":38,"title":"Servicio","description":"Revisar Router","assignedUserId":null,"clientId":2,"date":null,"duration":60,"status":0,"address":"31 Chiapas, Dolores Hidalgo Cuna de la Independencia Nacional, 37800, Mexico","gpsLat":null,"gpsLon":null,"attachments":[],"tasks":[]},"entityBeforeEdit":null}} + //Extraer el valor de title en una variable y concatenarle como prefijo la cadena "[SINENVIONOTIFICACION]" por ejemplo: "[NOTIFICACION-PENDIENTE]Servicio" + $title = $jsonData['extraData']['entity']['title']; + $title = '[NOTIFICACION-PENDIENTE]' . $title; + $this->ucrmApi = UcrmApi::create(); + $responsePatch = $this->ucrmApi->patch('scheduling/jobs/' . $jsonData['entityId'], [ + 'title' => $title, + ]); + + $this->logger->debug('Respuesta de la API al agregar el trabajo: ' . json_encode($responsePatch) . PHP_EOL); + + } else if ($notification->eventName === 'job.edit') { + $this->logger->debug('Se actualiza un trabajo' . PHP_EOL); + // $this->logger->debug('Valor de json_data: ' . json_encode($jsonData)); + + // Validar que 'extraData' existe y contiene las claves necesarias + if ( + isset($jsonData['extraData']['entityBeforeEdit']) && + isset($jsonData['extraData']['entity']) + ) { + $entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit']; + $entity = $jsonData['extraData']['entity']; + + $this->logger->debug('Validando claves dentro de entityBeforeEdit y entity'); + + // Validar que 'assignedUserId' existe en ambas entidades + if (array_key_exists('assignedUserId', $entityBeforeEdit) && array_key_exists('assignedUserId', $entity)) { + // $this->logger->debug('Los datos entityBeforeEdit y entity contienen el campo assignedUserId'); + + $assignedUserIdBefore = $entityBeforeEdit['assignedUserId']; + $assignedUserIdAfter = $entity['assignedUserId']; + $dateBefore = $entityBeforeEdit['date']; + $dateAfter = $entity['date']; + $statusBefore = $entityBeforeEdit['status']; + $statusAfter = $entity['status']; + $title = $entityBeforeEdit['title']; + $pendingPrefix = '[NOTIFICACION-PENDIENTE]'; // Prefijo para trabajos pendientes de notificación + $currentTitle = $entity['title'] ?? ''; // Obtener el título actual + + //Valores de status y su significado: 0 = abierto, 1= En curso, 2 = Cerrado + + if ($statusAfter == 1) { + // Comprobar si 'assignedUserId' cambió + // if (($assignedUserIdBefore === null && $assignedUserIdAfter != null) || ($statusBefore == 0 && $statusAfter == 1)) { //Si el campo "assignedUserId" cambió de null a un valor + // $this->logger->debug('El instalador cambió de null a un valor'); + + // $this->notifierFacade->verifyJobActionToDo($jsonData); // Se envía notificación de trabajo asignado + // } + + //Si el campo status cambió de 0 a 1 y title comienza con el prefijo [NOTIFICACION-PENDIENTE] + if (($statusBefore == 0 && $statusAfter == 1) && strpos($currentTitle, $pendingPrefix) !== false) { // Se envía notificación de trabajo asignado + $this->logger->debug('El instalador cambió de null a un valor'); + $this->notifierFacade->verifyJobActionToDo($jsonData, false, false); + } else if (($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore) && ($dateBefore === $dateAfter)) { //Si el campo "assignedUserId" cambió de un valor a otro y la fecha no cambió + $this->logger->debug('No hay reprogramación de trabajo pero si hay cambio de instalador'); + $this->notifierFacade->verifyJobActionToDo($jsonData, false, true); // Se envía notificación de trabajo reasignado + } else if (($assignedUserIdBefore != null && $assignedUserIdBefore === $assignedUserIdAfter) && ($dateBefore != $dateAfter)) { //Si el campo "assignedUserId" no cambió y la fecha cambió + $this->logger->debug('Se reprogramó el trabajo pero no hubo cambio de instalador'); + $this->notifierFacade->verifyJobActionToDo($jsonData, true, false); // Se envía notificación de reprogramación de trabajo + } else if (($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore) && ($dateBefore != $dateAfter)) { + $this->logger->debug('Se reprogramó el trabajo y hubo cambio de instalador'); + $this->notifierFacade->verifyJobActionToDo($jsonData, true, true); // Se envía notificación de trabajo reasignado + } else if ($assignedUserIdBefore != null && $assignedUserIdAfter === null) { //Si el campo "assignedUserId" cambió de un valor a null + $this->logger->debug('El instalador cambió de un valor a null'); + $this->notifierFacade->verifyJobActionToDo($jsonData, null, true); // Se envía notificación de trabajo desasignado + } else { + $this->logger->debug('No hubo cambio en el instalador ni en la fecha'); + //$this->notifierFacade->verifyJobActionToDo($jsonData); // Se envía notificación de trabajo asignado + } + } + + } else { + $this->logger->warning('El campo assignedUserId no existe en entityBeforeEdit o entity'); + } + } else { + $this->logger->warning('Los datos entityBeforeEdit o entity no están presentes en extraData'); + } + + } + + //$this->notifierFacade->update($notification); + } catch (TwilioException $exception) { + $this->logger->error($exception->getMessage()); + } catch (\Exception $ex) { + $this->logger->error($ex->getMessage()); + $this->logger->info($ex->getTraceAsString()); + } + } +} diff --git a/composer.json b/composer.json old mode 100644 new mode 100755 diff --git a/composer.lock b/composer.lock old mode 100644 new mode 100755 diff --git a/data/config.json b/data/config.json old mode 100644 new mode 100755 index 58c8267..47dccd2 --- a/data/config.json +++ b/data/config.json @@ -1 +1 @@ -{"ipserver":"sistema.siip.mx","apitoken":"NftJK4BllFalBa1iCCjAww6T/opqDMhyCVyMoBLkq4CPHHUjLEoIsHyEaPwflMz1","debugMode":true,"logging_level":true,"ipServer":"sistema.siip.mx","apiTokenUcrm":"NftJK4BllFalBa1iCCjAww6T/opqDMhyCVyMoBLkq4CPHHUjLEoIsHyEaPwflMz1","apiTokenStripe":"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"} \ No newline at end of file +{"ipServer":"venus.siip.mx","apiTokenUcrm":"35lAikF7l9ua8KxmkXhekByNP0vj8Yc3o65bsfl08oN55VN2bdarTu1y3pP4tAXj","apiTokenStripe":"sk_test_51OkG0REFY1WEUtgRH6UxBK5pu80Aq5Iy8EcdPnf0cOWzuVLQTpyLCd7CbPzqMsWMafZOHElCxhEHF7g8boURjWlJ00tBwE0W1M","debugMode":true,"logging_level":true} \ No newline at end of file diff --git a/data/plugin.log b/data/plugin.log old mode 100644 new mode 100755 index daf33c5..76a4129 --- a/data/plugin.log +++ b/data/plugin.log @@ -2,3 +2,23 @@ Create Intent Result: {"success":true,"id":"pi_3Sf48SEFY1WEUtgR0s5i4ole","status Create Intent Result: {"success":true,"id":"pi_3Sf4IyEFY1WEUtgR1jQ2r4pM","status":"requires_action","amount":100,"currency":"mxn"} Create Intent Result: {"success":true,"id":"pi_3Sf5DMEFY1WEUtgR06kkTXuo","status":"requires_action","amount":200,"currency":"mxn"} Create Intent Result: {"success":true,"id":"pi_3Sf5EpEFY1WEUtgR1D1YsgLF","status":"requires_action","amount":150,"currency":"mxn"} +Create Intent Result: {"success":true,"id":"pi_3SfSt1EFY1WEUtgR1isPFtsV","status":"succeeded","amount":500,"currency":"mxn","next_action":null} +Create Intent Result: {"success":false,"error":"No such customer: 'contrase\u00f1a obtenida 2'"} +Create Intent Result: {"success":false,"error":"No such customer: 'contrase\u00f1a obtenida 2'"} +Create Intent Result: {"success":true,"id":"pi_3SfTCOEFY1WEUtgR1UdBNVDk","status":"requires_action","amount":600,"currency":"mxn","next_action":{"display_bank_transfer_instructions":{"amount_remaining":60000,"currency":"mxn","financial_addresses":[{"spei":{"account_holder_address":{"city":"Ciudad de M\u00e9xico","country":"MX","line1":"Av Paseo de la Reforma 180 piso 29 col Juarez","line2":null,"postal_code":"06600","state":"Ciudad de M\u00e9xico"},"account_holder_name":"SIIP INTERNET","bank_address":{"city":"Naucalpan","country":"MX","line1":"Blvd. Manuel Avila Camacho 1902","line2":"Planta Baja, Cd. Satelite","postal_code":"C.P. 53100","state":"Estado de Mexico"},"bank_code":"124","bank_name":"CITI","clabe":"124180257395215094"},"supported_networks":["spei"],"type":"spei"}],"hosted_instructions_url":"https:\/\/payments.stripe.com\/bank_transfers\/instructions\/test_YWNjdF8xT2tHMFJFRlkxV0VVdGdSLF9UY2lkNTgzSmNzV01iMWV1WWhnb2k4V3B6dVR5Y1NI0100Vwpk0GNP","reference":"836126","type":"mx_bank_transfer"},"type":"display_bank_transfer_instructions"}} +Create Intent Result: {"success":true,"id":"pi_3SfTEsEFY1WEUtgR0sUbrbKn","status":"requires_action","amount":600,"currency":"mxn","next_action":{"display_bank_transfer_instructions":{"amount_remaining":7000,"currency":"mxn","financial_addresses":[{"spei":{"account_holder_address":{"city":"Ciudad de M\u00e9xico","country":"MX","line1":"Av Paseo de la Reforma 180 piso 29 col Juarez","line2":null,"postal_code":"06600","state":"Ciudad de M\u00e9xico"},"account_holder_name":"SIIP INTERNET","bank_address":{"city":"Naucalpan","country":"MX","line1":"Blvd. Manuel Avila Camacho 1902","line2":"Planta Baja, Cd. Satelite","postal_code":"C.P. 53100","state":"Estado de Mexico"},"bank_code":"124","bank_name":"CITI MEXICO","clabe":"124180464584143838"},"supported_networks":["spei"],"type":"spei"}],"hosted_instructions_url":"https:\/\/payments.stripe.com\/bank_transfers\/instructions\/test_YWNjdF8xT2tHMFJFRlkxV0VVdGdSLF9UY2lnZHBDdzdXaVA0dlk1eGpnTDhXUW90dG43UXJJ01005zNS9IqA","reference":"891006","type":"mx_bank_transfer"},"type":"display_bank_transfer_instructions"}} +Create Intent Result: {"success":true,"id":"pi_3SfTFNEFY1WEUtgR0ARP09wI","status":"requires_action","amount":600,"currency":"mxn","next_action":{"display_bank_transfer_instructions":{"amount_remaining":60000,"currency":"mxn","financial_addresses":[{"spei":{"account_holder_address":{"city":"Ciudad de M\u00e9xico","country":"MX","line1":"Av Paseo de la Reforma 180 piso 29 col Juarez","line2":null,"postal_code":"06600","state":"Ciudad de M\u00e9xico"},"account_holder_name":"SIIP INTERNET","bank_address":{"city":"Naucalpan","country":"MX","line1":"Blvd. Manuel Avila Camacho 1902","line2":"Planta Baja, Cd. Satelite","postal_code":"C.P. 53100","state":"Estado de Mexico"},"bank_code":"124","bank_name":"CITI","clabe":"124180257395215094"},"supported_networks":["spei"],"type":"spei"}],"hosted_instructions_url":"https:\/\/payments.stripe.com\/bank_transfers\/instructions\/test_YWNjdF8xT2tHMFJFRlkxV0VVdGdSLF9UY2lnSUZJYzBqaUM2RlQzUHZNNXhMSDBia0NyQ0RG0100aPJCIE4P","reference":"287441","type":"mx_bank_transfer"},"type":"display_bank_transfer_instructions"}} +Create Intent Result: {"success":true,"id":"pi_3SfnSmEFY1WEUtgR0F4shJOv","status":"requires_action","amount":600,"currency":"mxn","next_action":{"display_bank_transfer_instructions":{"amount_remaining":60000,"currency":"mxn","financial_addresses":[{"spei":{"account_holder_address":{"city":"Ciudad de M\u00e9xico","country":"MX","line1":"Av Paseo de la Reforma 180 piso 29 col Juarez","line2":null,"postal_code":"06600","state":"Ciudad de M\u00e9xico"},"account_holder_name":"SIIP INTERNET","bank_address":{"city":"Naucalpan","country":"MX","line1":"Blvd. Manuel Avila Camacho 1902","line2":"Planta Baja, Cd. Satelite","postal_code":"C.P. 53100","state":"Estado de Mexico"},"bank_code":"124","bank_name":"CITI","clabe":"124180257395215094"},"supported_networks":["spei"],"type":"spei"}],"hosted_instructions_url":"https:\/\/payments.stripe.com\/bank_transfers\/instructions\/test_YWNjdF8xT2tHMFJFRlkxV0VVdGdSLF9UZDNabm04VGE5QjVRV0tCdkNvNnI2R0ZyeW5BY21y0100Cek337H9","reference":"723997","type":"mx_bank_transfer"},"type":"display_bank_transfer_instructions"}} +Create Intent Result: {"success":true,"id":"pi_3SfnwDEFY1WEUtgR1PKgbJlo","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3Sfo8lEFY1WEUtgR1cTsq4mZ","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3Sfpe1EFY1WEUtgR0eLGPAYb","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":false,"error":"Could not connect to Stripe (https:\/\/api.stripe.com\/v1\/payment_intents). Please check your internet connection and try again. If this problem persists, you should check Stripe's service status at https:\/\/twitter.com\/stripestatus, or let us know at support@stripe.com.\n\n(Network error [errno 28]: Operation timed out after 80001 milliseconds with 0 bytes received)"} +Create Intent Result: {"success":true,"id":"pi_3SfqxqEFY1WEUtgR19zL9aNr","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3Sfr6AEFY1WEUtgR1dhC2Ffj","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3SfrAkEFY1WEUtgR01zLxB4v","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3Sg6SCEFY1WEUtgR1NqBNPcb","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3Sg7rzEFY1WEUtgR0rGdMrvv","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3Sg9DQEFY1WEUtgR1Ye5k6G3","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3Sg9PLEFY1WEUtgR1iAOyHlS","status":"succeeded","amount":600,"currency":"mxn","next_action":null} +Create Intent Result: {"success":true,"id":"pi_3Sg9kHEFY1WEUtgR0UDlXu7T","status":"requires_action","amount":250,"currency":"mxn","next_action":{"display_bank_transfer_instructions":{"amount_remaining":25000,"currency":"mxn","financial_addresses":[{"spei":{"account_holder_address":{"city":"Ciudad de M\u00e9xico","country":"MX","line1":"Av Paseo de la Reforma 180 piso 29 col Juarez","line2":null,"postal_code":"06600","state":"Ciudad de M\u00e9xico"},"account_holder_name":"SIIP INTERNET","bank_address":{"city":"Naucalpan","country":"MX","line1":"Blvd. Manuel Avila Camacho 1902","line2":"Planta Baja, Cd. Satelite","postal_code":"C.P. 53100","state":"Estado de Mexico"},"bank_code":"124","bank_name":"CITI","clabe":"124180257395215094"},"supported_networks":["spei"],"type":"spei"}],"hosted_instructions_url":"https:\/\/payments.stripe.com\/bank_transfers\/instructions\/test_YWNjdF8xT2tHMFJFRlkxV0VVdGdSLF9UZFFiajNxbDd6bE9PNmgxOXNXSXZIRzlaaVAzd1pm0100rOmKqech","reference":"779272","type":"mx_bank_transfer"},"type":"display_bank_transfer_instructions"}} +Create Intent Result: {"success":true,"id":"pi_3Shc8dEFY1WEUtgR123bgp1n","status":"succeeded","amount":750,"currency":"mxn","next_action":null} diff --git a/img/vista-general.png b/img/vista-general.png old mode 100644 new mode 100755 diff --git a/main.php b/main.php old mode 100644 new mode 100755 diff --git a/manifest.json b/manifest.json old mode 100644 new mode 100755 diff --git a/public.php b/public.php old mode 100644 new mode 100755 index 301ec7b..9f159d7 --- a/public.php +++ b/public.php @@ -96,6 +96,59 @@ if ($action === 'create_intent') { exit; } +// 5. Webhook Handler +if ($action === 'webhook') { + $payload = @file_get_contents('php://input'); + $event = null; + + try { + $event = \Stripe\Event::constructFrom( + json_decode($payload, true) + ); + } catch(\UnexpectedValueException $e) { + http_response_code(400); + exit(); + } + + $log->appendLog("Webhook Received: " . $event->type); + + if ($event->type == 'payment_intent.succeeded') { + $paymentIntent = $event->data->object; + $clientId = $paymentIntent->metadata->clientId ?? null; + $amount = $paymentIntent->amount / 100; + $currency = $paymentIntent->currency; + + if ($clientId) { + $notes = "Pago procesado via Stripe PaymentIntent: " . $paymentIntent->id; + $res = $service->registerPayment($clientId, $amount, $currency, $notes); + $log->appendLog("Payment Register UCRM ($clientId): " . ($res ? 'Success' : 'Fail')); + } + } + elseif ($event->type == 'customer_cash_balance_transaction.created') { + $txn = $event->data->object; + // Check if money was applied to a payment object + if (isset($txn->applied_to_payment) && isset($txn->applied_to_payment->payment_intent)) { + $piId = $txn->applied_to_payment->payment_intent; + $paymentIntent = $service->getPaymentIntent($piId); + + if ($paymentIntent) { + $clientId = $paymentIntent->metadata->clientId ?? null; + $amount = abs($txn->net_amount) / 100; + $currency = $txn->currency; + + if ($clientId) { + $notes = "Pago via Transferencia (Cash Balance) aplicado a Intent: " . $piId; + $res = $service->registerPayment($clientId, $amount, $currency, $notes); + $log->appendLog("Cash Balance Payment Register UCRM ($clientId): " . ($res ? 'Success' : 'Fail')); + } + } + } + } + + http_response_code(200); + exit; +} + // --- VIEW (HTML) --- ?> @@ -596,6 +649,41 @@ if ($action === 'create_intent') { + + +