/** * Create Payment Intent for Order (v2 - with Payfrit fee structure) * * Fee Structure: * - Customer pays: Subtotal + tax + tip + 5% Payfrit fee + card processing (2.9% + $0.30) * - Restaurant receives: Subtotal - 5% Payfrit fee + tax + tip * - Payfrit receives: 10% of subtotal (5% from customer + 5% from restaurant) * * POST: { * BusinessID: int, * OrderID: int, * Subtotal: number (order subtotal in dollars, before fees), * Tax: number (tax amount in dollars), * Tip: number (optional, in dollars), * CustomerEmail: string (optional) * } */ response = { "OK": false }; try { requestData = deserializeJSON(toString(getHttpRequestData().content)); businessID = val(requestData.BusinessID ?: 0); orderID = val(requestData.OrderID ?: 0); subtotal = val(requestData.Subtotal ?: 0); tax = val(requestData.Tax ?: 0); tip = val(requestData.Tip ?: 0); customerEmail = requestData.CustomerEmail ?: ""; if (businessID == 0) { response["ERROR"] = "BusinessID is required"; writeOutput(serializeJSON(response)); abort; } if (orderID == 0) { response["ERROR"] = "OrderID is required"; writeOutput(serializeJSON(response)); abort; } if (subtotal <= 0) { response["ERROR"] = "Invalid subtotal"; writeOutput(serializeJSON(response)); abort; } // Use test keys stripeSecretKey = application.stripeSecretKey ?: "sk_test_LfbmDduJxTwbVZmvcByYmirw"; if (stripeSecretKey == "") { response["ERROR"] = "Stripe is not configured"; writeOutput(serializeJSON(response)); abort; } // Get business Stripe account qBusiness = queryExecute(" SELECT BusinessStripeAccountID, BusinessStripeOnboardingComplete, BusinessName FROM Businesses WHERE BusinessID = :businessID ", { businessID: businessID }, { datasource: "payfrit" }); if (qBusiness.recordCount == 0) { response["ERROR"] = "Business not found"; writeOutput(serializeJSON(response)); abort; } // Get order's delivery fee (if delivery order) qOrder = queryExecute(" SELECT OrderDeliveryFee, OrderTypeID FROM Orders WHERE OrderID = :orderID ", { orderID: orderID }, { datasource: "payfrit" }); deliveryFee = 0; if (qOrder.recordCount > 0 && qOrder.OrderTypeID == 3) { deliveryFee = val(qOrder.OrderDeliveryFee); } // For testing, allow orders even without Stripe Connect setup hasStripeConnect = qBusiness.BusinessStripeOnboardingComplete == 1 && len(trim(qBusiness.BusinessStripeAccountID)) > 0; // ============================================================ // FEE CALCULATION // ============================================================ customerFeePercent = 0.05; // 5% customer pays to Payfrit businessFeePercent = 0.05; // 5% business pays to Payfrit cardFeePercent = 0.029; // 2.9% Stripe fee cardFeeFixed = 0.30; // $0.30 Stripe fixed fee payfritCustomerFee = subtotal * customerFeePercent; payfritBusinessFee = subtotal * businessFeePercent; totalBeforeCardFee = subtotal + tax + tip + deliveryFee + payfritCustomerFee; cardFee = (totalBeforeCardFee * cardFeePercent) + cardFeeFixed; totalCustomerPays = totalBeforeCardFee + cardFee; // Convert to cents for Stripe totalAmountCents = round(totalCustomerPays * 100); totalPlatformFeeCents = round((payfritCustomerFee + payfritBusinessFee) * 100); // Create PaymentIntent httpService = new http(); httpService.setMethod("POST"); httpService.setUrl("https://api.stripe.com/v1/payment_intents"); httpService.setUsername(stripeSecretKey); httpService.setPassword(""); httpService.addParam(type="formfield", name="amount", value=totalAmountCents); httpService.addParam(type="formfield", name="currency", value="usd"); httpService.addParam(type="formfield", name="automatic_payment_methods[enabled]", value="true"); if (hasStripeConnect) { httpService.addParam(type="formfield", name="application_fee_amount", value=totalPlatformFeeCents); httpService.addParam(type="formfield", name="transfer_data[destination]", value=qBusiness.BusinessStripeAccountID); } httpService.addParam(type="formfield", name="metadata[order_id]", value=orderID); httpService.addParam(type="formfield", name="metadata[business_id]", value=businessID); httpService.addParam(type="formfield", name="description", value="Order ###orderID# at #qBusiness.BusinessName#"); if (customerEmail != "") { httpService.addParam(type="formfield", name="receipt_email", value=customerEmail); } result = httpService.send().getPrefix(); piData = deserializeJSON(result.fileContent); if (structKeyExists(piData, "error")) { response["ERROR"] = piData.error.message; writeOutput(serializeJSON(response)); abort; } // Fees are calculated dynamically, not stored in DB response["OK"] = true; response["CLIENT_SECRET"] = piData.client_secret; response["PAYMENT_INTENT_ID"] = piData.id; response["PUBLISHABLE_KEY"] = application.stripePublishableKey ?: "pk_test_sPBNzSyJ9HcEPJGC7dSo8NqN"; response["FEE_BREAKDOWN"] = { "SUBTOTAL": subtotal, "TAX": tax, "TIP": tip, "DELIVERY_FEE": deliveryFee, "PAYFRIT_FEE": payfritCustomerFee, "CARD_FEE": cardFee, "TOTAL": totalCustomerPays }; response["STRIPE_CONNECT_ENABLED"] = hasStripeConnect; } catch (any e) { response["ERROR"] = e.message; response["DETAIL"] = e.detail ?: ""; } writeOutput(serializeJSON(response));