How to Manage Status with cancellation process through the api ?

Dear Square Support Team,

I hope you are doing well.

I am currently integrating Square Orders and Payments APIs in my application. I am successfully able to create orders and process payments using the Orders API followed by the Payments API.

However, I am facing an issue related to order status management.

**Issue Description:**
When I create an order, I explicitly set the order state to **OPEN** during order creation. After that, I create the payment associated with the order. But immediately after the payment is successfully created, the order automatically moves to the **COMPLETED** state.

My requirement is different:

* When an order is created and payment is made, the order should remain in an **ACTIVE / OPEN** state.
* The order should only move to **COMPLETED** when I explicitly update it after fulfillment or delivery is finished.

**Current Implementation Flow:**

1. Create Order using Orders API
2. Set order state to `OPEN`
3. Create Payment using Payments API with `order_id`
4. Order automatically transitions to `COMPLETED`

**What I would like to understand:**

1. Is this automatic transition to `COMPLETED` expected behavior when the payment is captured successfully?
2. Is there a recommended way to keep the order in `OPEN` or `IN_PROGRESS` state after payment creation?
3. Should I be using the Fulfillment API or another mechanism to control the order lifecycle manually?
4. Is there a specific parameter or configuration that prevents the order from automatically completing after payment?

For reference, I am using:

* Orders API to create the order
* Payments API with `source_id = EXTERNAL`
* Square API Version: **2026-01-22**

Any guidance or best practices for managing order lifecycle states would be greatly appreciated.

Thank you for your support.

Best regards,
Mamta Kumavat
Flutter Developer

this is my code.


Future setSquareOrderData(OrderModel orderModel, String paymentId) async {
try {
ShowToastDialog.showLoader("Please wait.");
String currency = "AUD";
NewOrderModel newOrderModel = NewOrderModel();
SquareOrder squareOrder = SquareOrder();

newOrderModel.idempotency_key = orderModel.id;
squareOrder.state = OrderState.open.displayName;
squareOrder.customer_id = orderModel.authorID;
squareOrder.reference_id = Constant.getUuid();
squareOrder.location_id = vendorModel.value.locationId;

// ─────────────────────────────────────────
// TAXES — define at order level with uid
// NO applied_money here — Square calculates it
// ─────────────────────────────────────────
List<OrderLineItemTax> newTaxSetting = [];

void addTax(dynamic tax, int orderSubtotal) {
if (tax == null) return;
if (tax.enable == false) return;
if (tax.tax == null || double.parse(tax.tax.toString()) <= 0) return;

String uid = Constant.getUuid();
String scope = tax.scope == 'product' ? "LINE_ITEM" : "ORDER";

if (tax.type == "percentage") {
// Direct percentage tax
newTaxSetting.add(OrderLineItemTax(
uid: uid,
name: tax.title ?? "Tax",
orderTaxType: "ADDITIVE",
scope: scope,
percentage: tax.tax.toString(),
));
} else if (tax.type == "fix") {
// Convert fixed amount to percentage
// percentage = (fixedAmount / orderSubtotal) * 100
if (orderSubtotal <= 0) {
print("⚠️ Skipping ${tax.title} — subtotal is 0");
return;
}

int fixedAmount = Constant.parseAmount(tax.tax.toString());
double percentage = (fixedAmount / orderSubtotal) * 100;
String percentageStr = percentage.toStringAsFixed(2); // e.g "1.19"

print(
"Fixed tax ${tax.title}: $fixedAmount / $orderSubtotal * 100 = $percentageStr%");

newTaxSetting.add(OrderLineItemTax(
uid: uid,
name: tax.title ?? "Tax",
orderTaxType: "ADDITIVE",
scope: scope,
percentage: percentageStr, // converted to percentage
));
}
}

// Calculate order subtotal first
int orderSubtotal = 0;
for (var product in orderModel.products!) {
int price = double.parse(product.discountPrice.toString()) <= 0
? Constant.parseAmount(product.price.toString())
: Constant.parseAmount(product.discountPrice.toString());
int qty = int.parse(product.quantity.toString());
orderSubtotal += price * qty;
}
print("Order subtotal: $orderSubtotal");
// Then add taxes with subtotal
orderModel.taxSetting?.forEach((tax) => addTax(tax, orderSubtotal));
orderModel.driverDeliveryTax
?.forEach((tax) => addTax(tax, orderSubtotal));
orderModel.packagingTax?.forEach((tax) => addTax(tax, orderSubtotal));
orderModel.platformTax?.forEach((tax) => addTax(tax, orderSubtotal));
print("Taxes being sent:");
for (var tax in newTaxSetting) {
print(" name: ${tax.name}, type: ${tax.orderTaxType}, "
"percentage: ${tax.percentage}"
"Money: ${tax.amount_money?.amount}");
}

// ─────────────────────────────────────────
// LINE ITEMS
// ─────────────────────────────────────────
List<LineItems> listLineItems = [];
List<int> extraPriceAmount = [];

for (var product in orderModel.products!) {
LineItems lineItems = LineItems();
lineItems.uid = Constant.squareOrderId(orderId: product.id!);
lineItems.name = product.name;
lineItems.quantity = product.quantity.toString();
lineItems.item_type = OrderLineItemItemType.item.displayName;
lineItems.metadata = product.variantInfo?.variantOptions;

// Base price
Money baseMoney = Money();
if (product.extrasPrice != null || product.extrasPrice != "0") {
/*extraPriceAmount.add(Constant.parseAmount(product.extrasPrice)!);*/
if (double.parse(product.discountPrice.toString()) <= 0) {
baseMoney.amount = Constant.parseAmount(product.price.toString()) +
Constant.parseAmount(product.extrasPrice);
} else {
baseMoney.amount =
Constant.parseAmount(product.discountPrice.toString()) +
Constant.parseAmount(product.extrasPrice);
}
} else {
if (double.parse(product.discountPrice.toString()) <= 0) {
baseMoney.amount = Constant.parseAmount(product.price.toString());
} else {
baseMoney.amount =
Constant.parseAmount(product.discountPrice.toString());
}
}
baseMoney.currency = currency;
lineItems.base_price_money = baseMoney;

// Applied taxes — only if product has taxSetting
// Reference uid from squareOrder.taxes
List<OrderLineItemAppliedTax> productTaxSetting = [];
if (product.taxSetting != null && product.taxSetting!.isNotEmpty) {
for (var productTax in product.taxSetting!) {
// Find matching tax uid from newTaxSetting
final matchingTax = newTaxSetting.firstWhere(
(t) => t.name == productTax.title,
orElse: () => newTaxSetting.first,
);

if (matchingTax.uid != null) {
Money taxMoney = Money(
amount: Constant.parseAmount(productTax.tax.toString()),
currency: currency,
);
productTaxSetting.add(OrderLineItemAppliedTax(
uid: Constant.getUuid(),
tax_uid: matchingTax.uid!, // Must match uid in taxes array
applied_money: taxMoney,
));
}
}
}

// Only set applied_taxes if not empty
if (productTaxSetting.isNotEmpty) {
lineItems.applied_taxes = productTaxSetting;
}

/* // Applied discounts — only if discount exists
if (lineItemDiscount.isNotEmpty) {
lineItems.applied_discounts = lineItemDiscount
.map((d) => OrderLineItemAppliedDiscount(
uid: Constant.getUuid(),
discount_uid: d.uid!, // Must match uid in discounts array
))
.toList();
}*/

// Extras as note, NOT as service charge
// (service charges need to be defined at order level)
if (Constant.parseAmount(product.extrasPrice.toString()) > 0) {
lineItems.note = "Extras Addons Price: ${product.extrasPrice}";
}

listLineItems.add(lineItems);
}

squareOrder.line_items = listLineItems;

// ─────────────────────────────────────────
// DISCOUNTS — define at order level with uid
// ─────────────────────────────────────────
List<OrderLineItemDiscount> lineItemDiscount = [];
if (orderModel.discount != null &&
Constant.parseAmount(orderModel.discount.toString()) > 0) {
Money discountMoney = Money(
amount: Constant.parseAmount(orderModel.discount.toString()),
currency: currency,
);
lineItemDiscount.add(OrderLineItemDiscount(
uid: Constant.getUuid(),
name: "Discount", // name is required
amount_money: discountMoney,
scope: "ORDER", // scope is required
));
}

if (couponAmount.value != 0.0) {
Money couponDiscount = Money(
amount: Constant.parseAmount(couponAmount.value.toString()),
currency: currency,
);
lineItemDiscount.add(OrderLineItemDiscount(
uid: Constant.getUuid(),
name: "Coupon Discount", // name is required
amount_money: couponDiscount,
scope: "ORDER", // scope is required
));
}
if (specialDiscountAmount.value != 0.0) {
Money specialDiscount = Money(
amount: Constant.parseAmount(specialDiscountAmount.value.toString()),
currency: currency,
);
lineItemDiscount.add(OrderLineItemDiscount(
uid: Constant.getUuid(),
name: "Special Discount", // name is required
amount_money: specialDiscount,
scope: "ORDER", // scope is required
));
}
// squareOrder.discounts = lineItemDiscount;


// ─────────────────────────────────────────
// SERVICE CHARGES — define at order level with uid
// ─────────────────────────────────────────
List<OrderServiceCharge> serivceCharges = [];
if (packagingCharge.value != 0.0) {
OrderServiceCharge packagingCharges = OrderServiceCharge();
packagingCharges.uid = Constant.getUuid();
packagingCharges.name = "Packaging Charges";
packagingCharges.scope = "ORDER";
packagingCharges.calculation_phase = "TOTAL_PHASE";
packagingCharges.amount_money = Money(
amount: Constant.parseAmount(packagingCharge.value.toString()),
currency: currency,
);
serivceCharges.add(packagingCharges);
}

if (deliveryTips.value != 0.0) {
OrderServiceCharge deliveryTip = OrderServiceCharge();
deliveryTip.uid = Constant.getUuid();
deliveryTip.name = "Delivery Tips";
deliveryTip.scope = "ORDER";
deliveryTip.calculation_phase = "TOTAL_PHASE";
deliveryTip.amount_money = Money(
amount: Constant.parseAmount(deliveryTips.value.toString()),
currency: currency,
);
// serivceCharges.add(deliveryTip);
}
if (deliveryCharges.value != 0.0) {
OrderServiceCharge deliveryCharge = OrderServiceCharge();
deliveryCharge.uid = Constant.getUuid();
deliveryCharge.name = "Delivery Charges";
deliveryCharge.scope = "ORDER";
deliveryCharge.calculation_phase = "TOTAL_PHASE";
deliveryCharge.amount_money = Money(
amount: Constant.parseAmount(deliveryCharges.value.toString()),
currency: currency,
);
// serivceCharges.add(deliveryCharge);
}
if (platformFee.value != 0.0) {
OrderServiceCharge platformFees = OrderServiceCharge();
platformFees.uid = Constant.getUuid();
platformFees.name = "Platform Fee";
platformFees.scope = "ORDER";
platformFees.calculation_phase = "TOTAL_PHASE";
platformFees.amount_money = Money(
amount: Constant.parseAmount(platformFee.value.toString()),
currency: currency,
);
// serivceCharges.add(platformFees);
}

OrderServiceCharge productTax = OrderServiceCharge();
if (Constant.taxScope == 'product' && productTaxAmount.value != 0.0) {
productTax.uid = Constant.getUuid();
productTax.name = "Tax on item total";
productTax.scope = "LINE_ITEM";
productTax.calculation_phase = "TOTAL_PHASE";
productTax.amount_money = Money(
amount: Constant.parseAmount(productTaxAmount.value.toString()),
currency: currency,
);
serivceCharges.add(productTax);
} else {
productTax.uid = Constant.getUuid();
productTax.name = "Tax on Order total";
productTax.scope = "ORDER";
productTax.calculation_phase = "TOTAL_PHASE";
productTax.amount_money = Money(
amount: Constant.parseAmount(orderTaxAmount.value.toString()),
currency: currency,
);
serivceCharges.add(productTax);
}

double squareTotalTaxAmount = 0.0;
if (Constant.taxScope == 'product') {
if (totalTaxAmount.value > productTaxAmount.value) {
squareTotalTaxAmount = totalTaxAmount.value - productTaxAmount.value;
} else {
squareTotalTaxAmount = productTaxAmount.value - totalTaxAmount.value;
}
} else {
if (totalTaxAmount.value > orderTaxAmount.value) {
squareTotalTaxAmount = totalTaxAmount.value - orderTaxAmount.value;
} else {
squareTotalTaxAmount = orderTaxAmount.value - totalTaxAmount.value;
}
}

if (squareTotalTaxAmount != 0.0) {
OrderServiceCharge squareTotalTaxCharge = OrderServiceCharge();
squareTotalTaxCharge.uid = Constant.getUuid();
squareTotalTaxCharge.name = "Total Tax Amount";
squareTotalTaxCharge.scope = "ORDER";
squareTotalTaxCharge.calculation_phase = "TOTAL_PHASE";
squareTotalTaxCharge.amount_money = Money(
amount: Constant.parseAmount(squareTotalTaxAmount.toString()),
currency: currency,
);
// serivceCharges.add(squareTotalTaxCharge);
}

// squareOrder.service_charges = serivceCharges;


newOrderModel.order = squareOrder;
// Add this just before the API call
final jsonBody = jsonEncode(newOrderModel.toJson());
print("Final JSON: $jsonBody");

// ─────────────────────────────────────────
// API Call
// ─────────────────────────────────────────
final response = await http.post(
Uri.parse(SquareConfig.ordersUrl),
headers: {
'Authorization': 'Bearer ${vendorModel.value.squareToken}',
// 'Authorization': 'Bearer ${Constant.productionAccessToken}',
'Content-Type': 'application/json',
"Square-Version": "2026-01-22",
},
body: jsonEncode(newOrderModel.toJson()),
);

print("Response==>${response.body}");
final body = jsonDecode(response.body);
int totalOrderAmount = 0;
for (var amount in listLineItems) {
totalOrderAmount += amount.base_price_money!.amount!;
}
/*for (var extraAmount in extraPriceAmount) {
totalOrderAmount += extraAmount;
}
*/
if (body["order"] != null) {
String orderId = body["order"]["id"];
Map<String, dynamic> totalMap = (body['order']['total_money']);
Money totalMoney = Money.fromJson(totalMap);
print(
"PaymentId==>$paymentId---LocationId==>${squareOrder.location_id}");
createPayment(paymentId, orderId, orderModel, totalMoney.amount!);
} else {
print("Order creation failed: ${body["errors"]}");
ShowToastDialog.closeLoader();
}
} catch (e) {
ShowToastDialog.closeLoader();
log("Error fetching: $e");
}
}

Future createPayment(String paymentId, String orderId,
OrderModel orderModel, int totalOrderAmount) async {
final response = await http.post(
Uri.parse(SquareConfig.paymentsUrl),
headers: {
'Authorization': 'Bearer ${vendorModel.value.squareToken}',
// 'Authorization': 'Bearer ${Constant.productionAccessToken}',
'Content-Type': 'application/json',
"Square-Version": "2026-01-22",
},
body: jsonEncode({
"idempotency_key": "pay-${DateTime.now().millisecondsSinceEpoch}",
"source_id": "EXTERNAL",
"amount_money": {"amount": totalOrderAmount, "currency": "AUD"},
"order_id": orderId,
"location_id": vendorModel.value.locationId,
"external_details": {
// Your gateway details here
"type": "OTHER", // CARD / BANK_TRANSFER / OTHER
"source": "Wallet", // Your gateway name
"source_id": paymentId
}
}),
);
if (response.statusCode == 200) {
orderModel.square_order_id = orderId;
await FireStoreUtils.setOrder(orderModel).then(
(value) async {
await FireStoreUtils.getUserProfile(
orderModel.vendor!.author.toString())
.then(
(value) async {
if (value != null) {
if (orderModel.scheduleTime != null) {
await SendNotification.sendFcmMessage(
Constant.scheduleOrder, value.fcmToken ?? '', {});
} else {
await SendNotification.sendFcmMessage(
Constant.newOrderPlaced, value.fcmToken ?? '', {});
}
}
},
);
ShowToastDialog.closeLoader();
Get.off(const OrderPlacingScreen(),
arguments: {"orderModel": orderModel});
},
);
// await Constant.sendOrderEmail(orderModel: orderModel);
// ShowToastDialog.closeLoader();
Get.off(const OrderPlacingScreen(),
arguments: {"orderModel": orderModel});
} else {
ShowToastDialog.closeLoader();
}

print('Payment ==> ${response.body}');
}

97 Views
Message 1 of 2
Report
1 REPLY 1

Hey @noQ4Food,

I think you will get the most help by asking your question the developer forums. https://developer.squareup.com/forums/

https://sellersquared.com/
Free Tools for Square Sellers to save time and run their businesses
78 Views
Message 2 of 2
Report