# MakePayment - Advanced Documentation ## Overview Processes a subscription billing payment via Braintree, validating payment method and billing date. Creates a billing invoice, extends subscription by 30 days, and emails the invoice to configured billing recipients. Automatically reverses the transaction on settlement failure. ## Business Rules - The current user must be logged in, have a company selected, be a member of that company, and have the `ManageFirm` permission. - **Billing date checks**: - If no billing date is set, or the company is terminated and this is an automated (non-UI) call, the method exits silently with no payment. - If the current UTC time is before the billing date, the method logs an exception and exits (prevents premature billing). - **Zero-amount handling**: If the billing amount is zero or negative, the transaction ID is set to `"No Charge"` and no Braintree transaction is created, but the subscription is still extended and an invoice is created. - **Payment flow**: 1. The saved payment method is validated via `ValidateSavedPayment`. 2. A Braintree sale transaction is authorized with the billing amount, discount, and line item details. 3. The transaction is submitted for settlement. 4. If settlement fails, the transaction is voided. - **Subscription extension**: On successful payment, `DuesPaidThru` is set to 30 days from now. `TerminationDate` is reset to `1900-01-01` (effectively clearing any termination). - **Invoice creation**: A `BillingInvoices` record is created with transaction ID, company ID, date range, user count, discount code, total, amount, and discount amount. - **Atomicity**: The company update and invoice creation are wrapped in a database transaction. If either fails and a real payment was made, the Braintree transaction is voided. - **Post-payment**: Invoice is emailed to configured billing recipients. Email failures do not roll back the payment. - **Failure emails**: When called from the SecureApi (UI), `SendFailureEmail` is `false`, so payment failure emails are NOT sent. When called from automated tasks, `SendFailureEmail` is `true`. ## Permissions & Security - Requires `ManageFirm` permission (explicitly checked via `Person.HasPermissions`). - User must be logged in and a member of the current company. - Braintree gateway credentials are retrieved from application configuration (not passed by the caller). ## Data Flow 1. Authentication, company membership, and `ManageFirm` permission validated. 2. `GetBillingInfo()` retrieves current billing state (amount, user count, discount, billing date, termination date). 3. Billing date and termination checks performed; early exit if not applicable. 4. Premature billing check (current date vs billing date). 5. If amount > 0: a. Braintree gateway obtained via `GetGateway()`. b. Saved payment method validated. c. Braintree sale authorized (with retry logic via `Retry.ExecuteAsync`). d. Transaction submitted for settlement (with retry). e. On settlement failure: transaction voided, exception rethrown. 6. If amount <= 0: transaction ID set to `"No Charge"`. 7. `DuesPaidThru` calculated as `DateTime.UtcNow.AddDays(30).Date`. 8. Invoice object constructed. 9. Database transaction started. 10. Company entity updated (`DuesPaidThru`, `TerminationDate`, `Timestamp`). 11. Invoice entity created and saved. 12. Database transaction committed. 13. Invoice emailed to billing recipients. 14. On DB save failure with a real transaction: Braintree transaction voided, exception rethrown. 15. On any outer failure with `SendFailureEmail=true`: failure emails sent to billing recipients. 16. Company cache and all person caches for the company cleared. ## Side Effects - **Braintree transaction**: Sale authorized and settled (or voided on failure). - **Database writes**: Company record updated (DuesPaidThru, TerminationDate, Timestamp). BillingInvoices record created. - **Email**: Invoice emailed to billing recipients on success. Payment failure emails sent on failure (only when `SendFailureEmail=true`, which is NOT the case for UI calls). - **Cache invalidation**: Company cache and all person caches for the company are cleared after successful payment. This ensures the UI reflects the updated subscription state. - **Console logging**: Extensive `Console.WriteLine` logging throughout the payment flow for debugging in task/service contexts. ## Error Conditions - `RadoloException("You must be logged in...")` if not authenticated. - `RadoloException("You must have a company selected...")` if no company context. - `RadoloException("You can only manage a subscription for a company that you are part of...")` if user is not a firm member. - `RadoloException("You do not have the Manage Firm permission.")` if user lacks `ManageFirm`. - `RadoloException("The Firm, {name}[{id}], should not be billed until [{date}].")` for premature billing (logged, method exits). - `RadoloException("Payment Failed for {id}. {message}")` from Braintree sale failure. - `RadoloException("{message}")` from Braintree settlement failure (includes error code and attribute in exception data). - `RadoloException("There was a problem saving the payment information: {error}")` on company save failure. - `RadoloException("There was a problem saving the invoice: {error}")` on invoice save failure. - Braintree void failures during rollback are retried but may ultimately fail, leaving an orphaned authorization. ## Usage Notes - When called from the SecureApi, `StartSubscription` is `true` and `SendFailureEmail` is `false`. This means: - Terminated companies CAN make payments from the UI (reactivation). - Payment failure emails are not sent (the UI shows the error directly). - When called from automated tasks, `StartSubscription` is typically `false` and `SendFailureEmail` is `true`, meaning terminated companies are skipped and failures are emailed. - The payment method must already be saved in Braintree (via a separate `SavePaymentMethod` endpoint). This method does not accept payment details directly. - The 30-day subscription extension is fixed and not configurable -- it always extends from "now" regardless of the existing `DuesPaidThru` date. - The Braintree customer ID is the company ID (converted to string). - Amounts are rounded using `RoundMoney()` extension method for Braintree compatibility. - The `Retry.ExecuteAsync` wrapper provides automatic retry for transient Braintree failures. - If the Braintree sale succeeds but the database save fails, the Braintree transaction is voided. However, if the void also fails, the transaction remains in Braintree in an authorized state.