> ## Documentation Index
> Fetch the complete documentation index at: https://developers.firmly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 1-Step Checkout

> Implement frictionless checkout in minutes with Firmly's place-order API

<Info>
  **The simplest checkout flow possible**: Go from cart to completed order in a single API call. Perfect for merchants who want to maximize conversion with minimal friction.
</Info>

## Why 1-Step Checkout?

Traditional checkout flows require multiple steps:

1. Create cart → 2. Add items → 3. Set shipping → 4. Calculate tax → 5. Process payment → 6. Complete order

With Firmly's `place-order` API, you can do all of this in **one single API call**.

### Benefits

<CardGroup cols={3}>
  <Card title="Higher Conversion" icon="chart-line">
    Reduce checkout abandonment with fewer steps
  </Card>

  <Card title="Faster Integration" icon="clock">
    Implement in hours, not weeks
  </Card>

  <Card title="Less Complexity" icon="code">
    One endpoint to maintain instead of six
  </Card>
</CardGroup>

## How It Works

The `place-order` API combines cart creation, item addition, shipping calculation, and payment processing into a single atomic operation:

```mermaid theme={null}
graph LR
    A[Customer Data] --> B[place-order API]
    B --> C[Completed Order]
```

<Note>
  **Real-Time Updates**: The `place-order` API supports Server-Sent Events (SSE) for real-time order processing updates. Perfect for showing progress indicators during checkout.
</Note>

## Quick Implementation

<Steps>
  <Step title="Collect Customer Information">
    Gather items, shipping address, and credit card details in your checkout form
  </Step>

  <Step title="Encrypt Credit Card">
    Use Firmly's public key to JWE-encrypt card details for secure transmission
  </Step>

  <Step title="Call place-order API">
    Submit everything in one API call and handle the response
  </Step>
</Steps>

## Credit Card Encryption

<Warning>
  **Security First**: Never send raw credit card data to any API. Always encrypt card details using JWE (JSON Web Encryption) with Firmly's public key.
</Warning>

The credit card data must be encrypted as a JWE token containing:

```json theme={null}
{
  "number": "4111111111111111",      // Card number
  "name": "John Smith",              // Cardholder name
  "verification_value": "123",       // CVV/CVC
  "month": "08",                     // Expiry month (2 digits)
  "year": "2025"                     // Expiry year (4 digits)
}
```

## Complete Example

Here's a full implementation of 1-step checkout:

<CodeGroup>
  ```javascript JavaScript theme={null}
  // First, install the jose library: npm install jose
  import * as jose from 'jose';

  async function oneStepCheckout(checkoutData, domain, authToken) {
    // Step 1: Get public key for encryption
    const publicKeyResponse = await fetch(
      'https://cc.firmly.work/api/v1/payment/key',
      {
        headers: {
          'x-firmly-authorization': authToken
        }
      }
    );
    const publicKeyJWK = await publicKeyResponse.json();

    // Step 2: Encrypt credit card data using JWE
    const cardData = {
      number: checkoutData.cardNumber,
      name: checkoutData.cardholderName,
      verification_value: checkoutData.cvv,
      month: checkoutData.expMonth,  // e.g., "08"
      year: checkoutData.expYear     // e.g., "2025"
    };

    // Import the public key
    const publicKey = await jose.importJWK(publicKeyJWK, 'RSA-OAEP-256');
    
    // Create JWE encrypted card
    const encryptedCard = await new jose.CompactEncrypt(
      new TextEncoder().encode(JSON.stringify(cardData))
    )
      .setProtectedHeader({ 
        alg: 'RSA-OAEP-256', 
        enc: 'A256GCM',
        kid: publicKeyJWK.kid 
      })
      .encrypt(publicKey);

    // Step 3: Place order in one call
    const orderResponse = await fetch(
      `https://cc.firmly.work/api/v1/payment/domains/${domain}/place-order`,
      {
        method: 'POST',
        headers: {
          'x-firmly-authorization': authToken,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          items: [
            {
              variant_id: 'WS12-XS-Orange',
              quantity: 2
            }
          ],
          shipping_info: {
            first_name: 'John',
            last_name: 'Doe',
            address1: '123 Main St',
            city: 'New York',
            state_or_province: 'NY',
            postal_code: '10001',
            country: 'US',
            email: 'john@staging.luma.gift',
            phone: '555-1234'
          },
          billing_info: {
            // Same structure as shipping_info
            // Or omit if billing same as shipping
            first_name: 'John',
            last_name: 'Doe',
            address1: '123 Main St',
            city: 'New York',
            state_or_province: 'NY',
            postal_code: '10001',
            country: 'US',
            email: 'john@staging.luma.gift',
            phone: '555-1234'
          },
          encrypted_card: encryptedCard
        })
      }
    );

    // Handle response
    const result = await orderResponse.json();
    
    if (result.cart_id) {
      // Success! Order completed
      return {
        success: true,
        cartId: result.cart_id,
        orderNumber: result.platform_order_number
      };
    } else {
      // Handle error
      return {
        success: false,
        error: result.error
      };
    }
  }

  // Example usage:
  const checkoutData = {
    cardNumber: '4242424242424242',
    cardholderName: 'John Doe',
    cvv: '123',
    expMonth: '12',
    expYear: '2025'
  };

  const domain = 'staging.luma.gift';
  const authToken = 'YOUR_AUTH_TOKEN'; // Get this from browser session

  // Call the function
  oneStepCheckout(checkoutData, domain, authToken)
    .then(result => {
      if (result.success) {
        console.log('Order placed successfully!', result.orderNumber);
      } else {
        console.error('Order failed:', result.error);
      }
    });
  ```

  ```python Python theme={null}
  import requests
  import json
  from joserfc import jwt
  from joserfc.jwe import encrypt_compact
  from joserfc.jwk import RSAKey

  def one_step_checkout(checkout_data, domain, auth_token):
      # Step 1: Get public key in JWK format (default)
      public_key_response = requests.get(
          "https://cc.firmly.work/api/v1/payment/key",
          headers={
              'x-firmly-authorization': auth_token
          }
      )
      public_key_jwk = public_key_response.json()
      
      # Step 2: Prepare card data
      card_data = {
          'number': checkout_data['card_number'],
          'name': checkout_data['cardholder_name'],
          'verification_value': checkout_data['cvv'],
          'month': checkout_data['exp_month'],    # e.g., "08"
          'year': checkout_data['exp_year']       # e.g., "2025"
      }
      
      # Step 3: Encrypt using JWE with JWK
      # Import the JWK
      public_key = RSAKey.import_key(public_key_jwk)
      
      # Create JWE encrypted card
      protected_header = {
          "alg": "RSA-OAEP-256",
          "enc": "A256GCM",
          "kid": public_key_jwk['kid']  # Get kid from JWK response
      }
      
      encrypted_card = encrypt_compact(
          json.dumps(card_data).encode('utf-8'),
          public_key,
          algorithm="RSA-OAEP-256",
          encryption="A256GCM",
          protected=protected_header
      )
      
      # Step 4: Place order
      order_data = {
          'items': [
              {
                  'variant_id': 'WS12-XS-Orange',
                  'quantity': 2
              }
          ],
          'shipping_info': {
              'first_name': 'John',
              'last_name': 'Doe',
              'address1': '123 Main St',
              'city': 'New York',
              'state_or_province': 'NY',
              'postal_code': '10001',
              'country': 'US',
              'email': 'john@staging.luma.gift',
              'phone': '555-1234'
          },
          'billing_info': {
              # Same structure as shipping_info
              'first_name': 'John',
              'last_name': 'Doe',
              'address1': '123 Main St',
              'city': 'New York',
              'state_or_province': 'NY',
              'postal_code': '10001',
              'country': 'US',
              'email': 'john@staging.luma.gift',
              'phone': '555-1234'
          },
          'encrypted_card': encrypted_card
      }
      
      response = requests.post(
          f"https://cc.firmly.work/api/v1/payment/domains/{domain}/place-order",
          headers={
              'x-firmly-authorization': auth_token,
              'Content-Type': 'application/json'
          },
          json=order_data
      )
      
      result = response.json()
      
      if 'cart_id' in result:
          return {
              'success': True,
              'cart_id': result['cart_id'],
              'order_number': result['platform_order_number']
          }
      else:
          return {
              'success': False,
              'error': result.get('error')
          }

  # Example usage:
  checkout_data = {
      'card_number': '4242424242424242',
      'cardholder_name': 'John Doe',
      'cvv': '123',
      'exp_month': '12',
      'exp_year': '2025'
  }

  domain = 'staging.luma.gift'
  auth_token = 'YOUR_AUTH_TOKEN'  # Get this from browser session

  # Call the function
  result = one_step_checkout(checkout_data, domain, auth_token)
  if result['success']:
      print(f"Order placed successfully! Order Number: {result['order_number']}")
  else:
      print(f"Order failed: {result['error']}")
  ```

  ```java Java theme={null}
  // Dependencies: 
  // - com.nimbusds:nimbus-jose-jwt:9.31
  // - com.fasterxml.jackson.core:jackson-databind:2.15.2
  import com.nimbusds.jose.*;
  import com.nimbusds.jose.crypto.RSAEncrypter;
  import com.nimbusds.jose.jwk.RSAKey;
  import com.fasterxml.jackson.databind.ObjectMapper;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.net.URI;
  import java.util.Map;
  import java.util.HashMap;
  import java.util.List;

  public class OneStepCheckout {
      private static final String API_BASE = "https://cc.firmly.work";
      private static final ObjectMapper mapper = new ObjectMapper();
      
      public static CheckoutResult performCheckout(
          CheckoutData checkoutData, 
          String domain, 
          String authToken
      ) throws Exception {
          
          // Step 1: Get public key in JWK format
          HttpClient client = HttpClient.newHttpClient();
          HttpRequest keyRequest = HttpRequest.newBuilder()
              .uri(URI.create(API_BASE + "/api/v1/payment/key"))
              .header("x-firmly-authorization", authToken)
              .GET()
              .build();
              
          HttpResponse<String> keyResponse = client.send(
              keyRequest, 
              HttpResponse.BodyHandlers.ofString()
          );
          
          Map<String, Object> jwkData = mapper.readValue(
              keyResponse.body(), 
              Map.class
          );
          
          // Step 2: Prepare card data
          Map<String, String> cardData = new HashMap<>();
          cardData.put("number", checkoutData.getCardNumber());
          cardData.put("name", checkoutData.getCardholderName());
          cardData.put("verification_value", checkoutData.getCvv());
          cardData.put("month", checkoutData.getExpMonth()); // e.g., "08"
          cardData.put("year", checkoutData.getExpYear());   // e.g., "2025"
          
          // Step 3: Encrypt using JWE
          RSAKey rsaKey = RSAKey.parse(mapper.writeValueAsString(jwkData));
          
          // Create JWE object
          JWEHeader header = new JWEHeader.Builder(
                  JWEAlgorithm.RSA_OAEP_256, 
                  EncryptionMethod.A256GCM
              )
              .keyID(jwkData.get("kid").toString())
              .build();
              
          Payload payload = new Payload(mapper.writeValueAsString(cardData));
          JWEObject jweObject = new JWEObject(header, payload);
          
          // Encrypt
          jweObject.encrypt(new RSAEncrypter(rsaKey));
          String encryptedCard = jweObject.serialize();
          
          // Step 4: Place order
          Map<String, Object> orderData = new HashMap<>();
          orderData.put("items", List.of(
              Map.of(
                  "variant_id", "WS12-XS-Orange",
                  "quantity", 2
              )
          ));
          
          Map<String, String> shippingInfo = new HashMap<>();
          shippingInfo.put("first_name", "John");
          shippingInfo.put("last_name", "Doe");
          shippingInfo.put("address1", "123 Main St");
          shippingInfo.put("city", "New York");
          shippingInfo.put("state_or_province", "NY");
          shippingInfo.put("postal_code", "10001");
          shippingInfo.put("country", "US");
          shippingInfo.put("email", "john@staging.luma.gift");
          shippingInfo.put("phone", "555-1234");
          
          orderData.put("shipping_info", shippingInfo);
          orderData.put("billing_info", shippingInfo); // Same as shipping
          orderData.put("encrypted_card", encryptedCard);
          
          HttpRequest orderRequest = HttpRequest.newBuilder()
              .uri(URI.create(API_BASE + "/api/v1/payment/domains/" + domain + "/place-order"))
              .header("x-firmly-authorization", authToken)
              .header("Content-Type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString(
                  mapper.writeValueAsString(orderData)
              ))
              .build();
              
          HttpResponse<String> orderResponse = client.send(
              orderRequest, 
              HttpResponse.BodyHandlers.ofString()
          );
          
          Map<String, Object> result = mapper.readValue(
              orderResponse.body(), 
              Map.class
          );
          
          if (result.containsKey("cart_id")) {
              return new CheckoutResult(
                  true,
                  result.get("cart_id").toString(),
                  result.get("platform_order_number").toString(),
                  null
              );
          } else {
              return new CheckoutResult(
                  false,
                  null,
                  null,
                  result.get("error").toString()
              );
          }
      }
  }

  // Supporting classes
  class CheckoutData {
      private String cardNumber;
      private String cardholderName;
      private String cvv;
      private String expMonth;
      private String expYear;
      // ... getters and setters
  }

  class CheckoutResult {
      private boolean success;
      private String cartId;
      private String orderNumber;
      private String error;
      
      public CheckoutResult(boolean success, String cartId, 
                           String orderNumber, String error) {
          this.success = success;
          this.cartId = cartId;
          this.orderNumber = orderNumber;
          this.error = error;
      }
      // ... getters
  }

  // Example usage:
  public static void main(String[] args) {
      CheckoutData checkoutData = new CheckoutData();
      checkoutData.setCardNumber("4242424242424242");
      checkoutData.setCardholderName("John Doe");
      checkoutData.setCvv("123");
      checkoutData.setExpMonth("12");
      checkoutData.setExpYear("2025");
      
      String domain = "staging.luma.gift";
      String authToken = "YOUR_AUTH_TOKEN"; // Get this from browser session
      
      try {
          CheckoutResult result = OneStepCheckout.performCheckout(
              checkoutData, 
              domain, 
              authToken
          );
          
          if (result.isSuccess()) {
              System.out.println("Order placed successfully! Order Number: " + 
                               result.getOrderNumber());
          } else {
              System.out.println("Order failed: " + result.getError());
          }
      } catch (Exception e) {
          System.err.println("Error: " + e.getMessage());
      }
  }
  ```

  ```php PHP theme={null}
  <?php
  // First install: composer require web-token/jwt-framework
  use Jose\Component\Core\AlgorithmManager;
  use Jose\Component\Core\JWK;
  use Jose\Component\Encryption\Algorithm\KeyEncryption\RSAOAEP256;
  use Jose\Component\Encryption\Algorithm\ContentEncryption\A256GCM;
  use Jose\Component\Encryption\Compression\CompressionMethodManager;
  use Jose\Component\Encryption\Compression\Deflate;
  use Jose\Component\Encryption\JWEBuilder;
  use Jose\Component\Encryption\Serializer\CompactSerializer;

  function oneStepCheckout($checkoutData, $domain, $authToken) {
      // Step 1: Get public key in JWK format
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, 
          "https://cc.firmly.work/api/v1/payment/key");
      curl_setopt($ch, CURLOPT_HTTPHEADER, [
          "x-firmly-authorization: {$authToken}"
      ]);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      $publicKeyResponse = curl_exec($ch);
      $publicKeyData = json_decode($publicKeyResponse, true);
      curl_close($ch);
      
      // Step 2: Create JWK from public key
      $jwk = new JWK($publicKeyData);
      
      // Step 3: Prepare card data
      $cardData = [
          'number' => $checkoutData['cardNumber'],
          'name' => $checkoutData['cardholderName'],
          'verification_value' => $checkoutData['cvv'],
          'month' => $checkoutData['expMonth'],    // e.g., "08"
          'year' => $checkoutData['expYear']       // e.g., "2025"
      ];
      
      // Step 4: Create JWE
      $algorithmManager = new AlgorithmManager([
          new RSAOAEP256(),
          new A256GCM(),
      ]);
      
      $compressionMethodManager = new CompressionMethodManager([
          new Deflate(),
      ]);
      
      $jweBuilder = new JWEBuilder(
          $algorithmManager,
          $algorithmManager,
          $compressionMethodManager
      );
      
      $payload = json_encode($cardData);
      
      $jwe = $jweBuilder
          ->create()
          ->withPayload($payload)
          ->withSharedProtectedHeader([
              'alg' => 'RSA-OAEP-256',
              'enc' => 'A256GCM',
              'kid' => $publicKeyData['kid']
          ])
          ->addRecipient($jwk)
          ->build();
      
      $serializer = new CompactSerializer();
      $encryptedCard = $serializer->serialize($jwe, 0);
      
      // Step 5: Place order
      $orderData = [
          'items' => [
              [
                  'variant_id' => 'WS12-XS-Orange',
                  'quantity' => 2
              ]
          ],
          'shipping_info' => [
              'first_name' => 'John',
              'last_name' => 'Doe',
              'address1' => '123 Main St',
              'city' => 'New York',
              'state_or_province' => 'NY',
              'postal_code' => '10001',
              'country' => 'US',
              'email' => 'john@staging.luma.gift',
              'phone' => '555-1234'
          ],
          'billing_info' => [
              // Same structure as shipping_info
              'first_name' => 'John',
              'last_name' => 'Doe',
              'address1' => '123 Main St',
              'city' => 'New York',
              'state_or_province' => 'NY',
              'postal_code' => '10001',
              'country' => 'US',
              'email' => 'john@staging.luma.gift',
              'phone' => '555-1234'
          ],
          'encrypted_card' => $encryptedCard
      ];
      
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, 
          "https://cc.firmly.work/api/v1/payment/domains/{$domain}/place-order");
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_HTTPHEADER, [
          "x-firmly-authorization: {$authToken}",
          "Content-Type: application/json"
      ]);
      curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($orderData));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      
      $response = curl_exec($ch);
      $result = json_decode($response, true);
      curl_close($ch);
      
      if (isset($result['cart_id'])) {
          return [
              'success' => true,
              'cart_id' => $result['cart_id'],
              'order_number' => $result['platform_order_number']
          ];
      } else {
          return [
              'success' => false,
              'error' => $result['error'] ?? 'Unknown error'
          ];
      }
  }

  // Example usage:
  $checkoutData = [
      'cardNumber' => '4242424242424242',
      'cardholderName' => 'John Doe',
      'cvv' => '123',
      'expMonth' => '12',
      'expYear' => '2025'
  ];

  $domain = 'staging.luma.gift';
  $authToken = 'YOUR_AUTH_TOKEN'; // Get this from browser session

  // Call the function
  $result = oneStepCheckout($checkoutData, $domain, $authToken);
  if ($result['success']) {
      echo "Order placed successfully! Order Number: " . $result['order_number'];
  } else {
      echo "Order failed: " . $result['error'];
  }
  ?>
  ```
</CodeGroup>

## Important Security Notes

<Warning>
  **Critical Security Requirements:**

  1. **Never store card data** - Card details should only exist in memory during encryption
  2. **Always use HTTPS** - All API calls must be over secure connections
  3. **PCI Compliance** - Ensure your implementation follows PCI DSS guidelines
  4. **Server-side encryption** - Never expose encryption logic to client-side JavaScript
  5. **Validate SSL certificates** - Ensure you're connecting to genuine Firmly endpoints
</Warning>

## Real-Time Features with SSE

<Info>
  **Server-Sent Events Support**: Both `place-order` endpoints (v1 and v2) support SSE for real-time order status updates. This enables you to show live progress during checkout processing.
</Info>

### Using SSE for Live Updates

```javascript theme={null}
// Option 1: Use SSE by setting Accept header
const domain = 'staging.luma.gift';
const authToken = 'YOUR_AUTH_TOKEN'; // Get this from browser session
const orderData = {
  items: [{ variant_id: 'WS12-XS-Orange', quantity: 1 }],
  shipping_info: { /* ... */ },
  billing_info: { /* ... */ },
  encrypted_card: encryptedCard
};

const response = await fetch(
  `https://cc.firmly.work/api/v1/payment/domains/${domain}/place-order`,
  {
    method: 'POST',
    headers: {
      'x-firmly-authorization': authToken,
      'Content-Type': 'application/json',
      'Accept': 'text/event-stream'  // Enable SSE
    },
    body: JSON.stringify(orderData)
  }
);

// Read the event stream
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  const chunk = decoder.decode(value);
  // Parse SSE events from chunk
  console.log('Event:', chunk);
}
```

### SSE Event Types

* **`order_processing`**: Order is being validated and processed
* **`payment_processing`**: Payment is being authorized
* **`order_completed`**: Order successfully placed with order details
* **`error`**: Processing failed with error details

## When to Use 1-Step Checkout

<Tabs>
  <Tab title="Perfect For">
    * Single vendor stores
    * Standard shipping only
    * High-volume, low-complexity products
    * Mobile-first experiences
  </Tab>

  <Tab title="Consider Alternatives If">
    * Multiple shipments needed
    * Complex shipping options (scheduled delivery)
    * B2B with special requirements
    * Require extensive customization
  </Tab>
</Tabs>

## Error Handling

The `place-order` API provides detailed error responses:

```json theme={null}
{
  "code": 400,
  "error": "InvalidAddress",
  "description": "Shipping address could not be validated"
}
```

Common error scenarios:

* **InvalidAddress**: Address validation failed
* **InsufficientStock**: Product not available
* **PaymentFailed**: Card declined or invalid
* **InvalidVariant**: Product SKU not found
* **InvalidCard**: Card encryption or validation failed

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="book" href="/api-reference/overview">
    Complete place-order API documentation
  </Card>

  <Card title="Payment Encryption" icon="lock" href="/api-reference/payment/get-public-key">
    Learn about secure payment handling
  </Card>

  <Card title="Simple Cart Guide" icon="shopping-cart" href="/guides/simple-cart-api-guide">
    Alternative multi-step approach
  </Card>

  <Card title="Testing Guide" icon="flask" href="/api-reference/authentication/browser-session">
    Set up test environment
  </Card>
</CardGroup>

## Support

Need help implementing 1-step checkout?

* **Integration Support**: [support@firmly.work](mailto:support@firmly.work)
* **API Status**: [status.firmly.work](https://status.firmly.work)
* **GitHub**: [Report issues](https://github.com/firmly/api-docs)
