Beispiel #1
0
        public async Task <bool> UpdatePaymentMethodAsync(ISubscriber subscriber, PaymentMethodType paymentMethodType,
                                                          string paymentToken)
        {
            if (subscriber == null)
            {
                throw new ArgumentNullException(nameof(subscriber));
            }

            if (subscriber.Gateway.HasValue && subscriber.Gateway.Value != GatewayType.Stripe)
            {
                throw new GatewayException("Switching from one payment type to another is not supported. " +
                                           "Contact us for assistance.");
            }

            var createdCustomer = false;

            Braintree.Customer braintreeCustomer        = null;
            string             stipeCustomerSourceToken = null;
            var stripeCustomerMetadata = new Dictionary <string, string>();
            var stripePaymentMethod    = paymentMethodType == PaymentMethodType.Card ||
                                         paymentMethodType == PaymentMethodType.BankAccount;

            var      cardService     = new CardService();
            var      bankSerice      = new BankAccountService();
            var      customerService = new CustomerService();
            Customer customer        = null;

            if (!string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId))
            {
                customer = await customerService.GetAsync(subscriber.GatewayCustomerId);

                if (customer.Metadata?.Any() ?? false)
                {
                    stripeCustomerMetadata = customer.Metadata;
                }
            }

            var hadBtCustomer = stripeCustomerMetadata.ContainsKey("btCustomerId");

            if (stripePaymentMethod)
            {
                stipeCustomerSourceToken = paymentToken;
            }
            else if (paymentMethodType == PaymentMethodType.PayPal)
            {
                if (hadBtCustomer)
                {
                    var pmResult = await _btGateway.PaymentMethod.CreateAsync(new Braintree.PaymentMethodRequest
                    {
                        CustomerId         = stripeCustomerMetadata["btCustomerId"],
                        PaymentMethodNonce = paymentToken
                    });

                    if (pmResult.IsSuccess())
                    {
                        var customerResult = await _btGateway.Customer.UpdateAsync(
                            stripeCustomerMetadata["btCustomerId"], new Braintree.CustomerRequest
                        {
                            DefaultPaymentMethodToken = pmResult.Target.Token
                        });

                        if (customerResult.IsSuccess() && customerResult.Target.PaymentMethods.Length > 0)
                        {
                            braintreeCustomer = customerResult.Target;
                        }
                        else
                        {
                            await _btGateway.PaymentMethod.DeleteAsync(pmResult.Target.Token);

                            hadBtCustomer = false;
                        }
                    }
                    else
                    {
                        hadBtCustomer = false;
                    }
                }

                if (!hadBtCustomer)
                {
                    var customerResult = await _btGateway.Customer.CreateAsync(new Braintree.CustomerRequest
                    {
                        PaymentMethodNonce = paymentToken,
                        Email = subscriber.BillingEmailAddress(),
                        Id    = subscriber.BraintreeCustomerIdPrefix() + subscriber.Id.ToString("N").ToLower() +
                                Utilities.CoreHelpers.RandomString(3, upper: false, numeric: false),
                        CustomFields = new Dictionary <string, string>
                        {
                            [subscriber.BraintreeIdField()] = subscriber.Id.ToString()
                        }
                    });

                    if (!customerResult.IsSuccess() || customerResult.Target.PaymentMethods.Length == 0)
                    {
                        throw new GatewayException("Failed to create PayPal customer record.");
                    }

                    braintreeCustomer = customerResult.Target;
                }
            }
            else
            {
                throw new GatewayException("Payment method is not supported at this time.");
            }

            if (stripeCustomerMetadata.ContainsKey("btCustomerId"))
            {
                if (braintreeCustomer?.Id != stripeCustomerMetadata["btCustomerId"])
                {
                    var nowSec = Utilities.CoreHelpers.ToEpocSeconds(DateTime.UtcNow);
                    stripeCustomerMetadata.Add($"btCustomerId_{nowSec}", stripeCustomerMetadata["btCustomerId"]);
                }
                stripeCustomerMetadata["btCustomerId"] = braintreeCustomer?.Id;
            }
            else if (!string.IsNullOrWhiteSpace(braintreeCustomer?.Id))
            {
                stripeCustomerMetadata.Add("btCustomerId", braintreeCustomer.Id);
            }

            try
            {
                if (customer == null)
                {
                    customer = await customerService.CreateAsync(new CustomerCreateOptions
                    {
                        Description = subscriber.BillingName(),
                        Email       = subscriber.BillingEmailAddress(),
                        SourceToken = stipeCustomerSourceToken,
                        Metadata    = stripeCustomerMetadata
                    });

                    subscriber.Gateway           = GatewayType.Stripe;
                    subscriber.GatewayCustomerId = customer.Id;
                    createdCustomer = true;
                }

                if (!createdCustomer)
                {
                    string defaultSourceId = null;
                    if (stripePaymentMethod)
                    {
                        if (paymentToken.StartsWith("btok_"))
                        {
                            var bankAccount = await bankSerice.CreateAsync(customer.Id, new BankAccountCreateOptions
                            {
                                SourceToken = paymentToken
                            });

                            defaultSourceId = bankAccount.Id;
                        }
                        else
                        {
                            var card = await cardService.CreateAsync(customer.Id, new CardCreateOptions
                            {
                                SourceToken = paymentToken,
                            });

                            defaultSourceId = card.Id;
                        }
                    }

                    foreach (var source in customer.Sources.Where(s => s.Id != defaultSourceId))
                    {
                        if (source is BankAccount)
                        {
                            await bankSerice.DeleteAsync(customer.Id, source.Id);
                        }
                        else if (source is Card)
                        {
                            await cardService.DeleteAsync(customer.Id, source.Id);
                        }
                    }

                    customer = await customerService.UpdateAsync(customer.Id, new CustomerUpdateOptions
                    {
                        Metadata      = stripeCustomerMetadata,
                        DefaultSource = defaultSourceId
                    });
                }
            }
            catch (Exception e)
            {
                if (braintreeCustomer != null && !hadBtCustomer)
                {
                    await _btGateway.Customer.DeleteAsync(braintreeCustomer.Id);
                }
                throw e;
            }

            return(createdCustomer);
        }
Beispiel #2
0
        public async Task <bool> PreviewUpcomingInvoiceAndPayAsync(ISubscriber subscriber, string planId,
                                                                   List <InvoiceSubscriptionItemOptions> subItemOptions, int prorateThreshold = 500)
        {
            var invoiceService     = new InvoiceService();
            var invoiceItemService = new InvoiceItemService();

            var pendingInvoiceItems = invoiceItemService.ListAutoPaging(new InvoiceItemListOptions
            {
                CustomerId = subscriber.GatewayCustomerId
            }).ToList().Where(i => i.InvoiceId == null);
            var pendingInvoiceItemsDict = pendingInvoiceItems.ToDictionary(pii => pii.Id);

            var upcomingPreview = await invoiceService.UpcomingAsync(new UpcomingInvoiceOptions
            {
                CustomerId        = subscriber.GatewayCustomerId,
                SubscriptionId    = subscriber.GatewaySubscriptionId,
                SubscriptionItems = subItemOptions
            });

            var itemsForInvoice = upcomingPreview.Lines?.Data?
                                  .Where(i => pendingInvoiceItemsDict.ContainsKey(i.Id) || (i.Plan.Id == planId && i.Proration));
            var invoiceAmount = itemsForInvoice?.Sum(i => i.Amount) ?? 0;
            var invoiceNow    = invoiceAmount >= prorateThreshold;

            if (invoiceNow)
            {
                // Owes more than prorateThreshold on next invoice.
                // Invoice them and pay now instead of waiting until next billing cycle.

                Invoice invoice             = null;
                var     createdInvoiceItems = new List <InvoiceItem>();
                Braintree.Transaction braintreeTransaction = null;
                try
                {
                    foreach (var ii in itemsForInvoice)
                    {
                        if (pendingInvoiceItemsDict.ContainsKey(ii.Id))
                        {
                            continue;
                        }
                        var invoiceItem = await invoiceItemService.CreateAsync(new InvoiceItemCreateOptions
                        {
                            Currency       = ii.Currency,
                            Description    = ii.Description,
                            CustomerId     = subscriber.GatewayCustomerId,
                            SubscriptionId = ii.SubscriptionId,
                            Discountable   = ii.Discountable,
                            Amount         = ii.Amount
                        });

                        createdInvoiceItems.Add(invoiceItem);
                    }

                    invoice = await invoiceService.CreateAsync(new InvoiceCreateOptions
                    {
                        Billing        = Billing.SendInvoice,
                        DaysUntilDue   = 1,
                        CustomerId     = subscriber.GatewayCustomerId,
                        SubscriptionId = subscriber.GatewaySubscriptionId
                    });

                    var invoicePayOptions = new InvoicePayOptions();
                    var customerService   = new CustomerService();
                    var customer          = await customerService.GetAsync(subscriber.GatewayCustomerId);

                    if (customer != null)
                    {
                        if (customer.Metadata.ContainsKey("btCustomerId"))
                        {
                            invoicePayOptions.PaidOutOfBand = true;
                            var btInvoiceAmount   = (invoiceAmount / 100M);
                            var transactionResult = await _btGateway.Transaction.SaleAsync(
                                new Braintree.TransactionRequest
                            {
                                Amount     = btInvoiceAmount,
                                CustomerId = customer.Metadata["btCustomerId"],
                                Options    = new Braintree.TransactionOptionsRequest
                                {
                                    SubmitForSettlement = true,
                                    PayPal = new Braintree.TransactionOptionsPayPalRequest
                                    {
                                        CustomField = $"{subscriber.BraintreeIdField()}:{subscriber.Id}"
                                    }
                                },
                                CustomFields = new Dictionary <string, string>
                                {
                                    [subscriber.BraintreeIdField()] = subscriber.Id.ToString()
                                }
                            });

                            if (!transactionResult.IsSuccess())
                            {
                                throw new GatewayException("Failed to charge PayPal customer.");
                            }

                            braintreeTransaction = transactionResult.Target;
                            await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
                            {
                                Metadata = new Dictionary <string, string>
                                {
                                    ["btTransactionId"]       = braintreeTransaction.Id,
                                    ["btPayPalTransactionId"] =
                                        braintreeTransaction.PayPalDetails.AuthorizationId
                                }
                            });
                        }
                    }

                    await invoiceService.PayAsync(invoice.Id, invoicePayOptions);
                }
                catch (Exception e)
                {
                    if (braintreeTransaction != null)
                    {
                        await _btGateway.Transaction.RefundAsync(braintreeTransaction.Id);
                    }
                    if (invoice != null)
                    {
                        await invoiceService.DeleteAsync(invoice.Id);

                        // Restore invoice items that were brought in
                        foreach (var item in pendingInvoiceItems)
                        {
                            var i = new InvoiceItemCreateOptions
                            {
                                Currency       = item.Currency,
                                Description    = item.Description,
                                CustomerId     = item.CustomerId,
                                SubscriptionId = item.SubscriptionId,
                                Discountable   = item.Discountable,
                                Metadata       = item.Metadata,
                                Quantity       = item.Quantity,
                                UnitAmount     = item.UnitAmount
                            };
                            await invoiceItemService.CreateAsync(i);
                        }
                    }
                    else
                    {
                        foreach (var ii in createdInvoiceItems)
                        {
                            await invoiceItemService.DeleteAsync(ii.Id);
                        }
                    }
                    throw e;
                }
            }
            return(invoiceNow);
        }