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

# Arcus - SPEI

> Connect to Arcus to accept SPEI bank transfer payments in Mexico.

export const connector = {
  displayName: "SPEI",
  method: "spei",
  features: "create_transaction direct_capture requires_webhook_setup verify_credentials",
  supportedCountries: "MX",
  supportedCurrencies: "MXN"
};

export const ConnectorRegions = ({data, kind, name: nameOverride}) => {
  const [query, setQuery] = useState("");
  const [open, setOpen] = useState(false);
  const isCountries = kind === "countries";
  const raw = data && (isCountries ? data.supportedCountries : data.supportedCurrencies);
  const codes = typeof raw === "string" ? raw.split(/\s+/).filter(Boolean) : Array.isArray(raw) ? raw : [];
  const DISPLAY_NAME_OVERRIDES = {
    authorizenet: "Authorize.net",
    cardpointe: "Fiserv CardPointe",
    dlocal: "dLocal",
    shift4i4go: "Shift4 i4go",
    tokenex: "TokenEx"
  };
  const rawName = data && data.displayName || "";
  const name = nameOverride || DISPLAY_NAME_OVERRIDES[rawName.toLowerCase()] || rawName || "This connector";
  const verb = isCountries ? "supports transactions from buyers in" : "supports processing payments in";
  const noun = isCountries ? "countries" : "currencies";
  if (codes.length === 0) return null;
  let displayNames = null;
  try {
    displayNames = new Intl.DisplayNames(["en"], {
      type: isCountries ? "region" : "currency"
    });
  } catch (e) {
    displayNames = null;
  }
  const resolve = code => {
    if (!displayNames) return null;
    try {
      const resolved = displayNames.of(code);
      return resolved && resolved !== code ? resolved : null;
    } catch (e) {
      return null;
    }
  };
  const MAJOR_CURRENCIES = ["USD", "EUR", "GBP", "CAD", "AUD", "JPY", "CHF", "CNY", "SGD", "HKD", "NZD", "SEK", "NOK", "DKK", "MXN", "BRL", "INR"];
  const items = codes.map(code => ({
    code,
    label: resolve(code)
  }));
  if (isCountries) {
    items.sort((a, b) => (a.label || a.code).localeCompare(b.label || b.code));
  } else {
    const rank = code => {
      const i = MAJOR_CURRENCIES.indexOf(code);
      return i === -1 ? MAJOR_CURRENCIES.length : i;
    };
    items.sort((a, b) => rank(a.code) - rank(b.code) || a.code.localeCompare(b.code));
  }
  if (codes.length <= 3) {
    const parts = items.map(it => isCountries || !it.label ? it.label || it.code : `${it.label} (${it.code})`);
    const joined = parts.length === 1 ? parts[0] : parts.length === 2 ? `${parts[0]} and ${parts[1]}` : `${parts.slice(0, -1).join(", ")}, and ${parts[parts.length - 1]}`;
    return <p>
        {name} {verb} {joined}.
      </p>;
  }
  const chipStyle = {
    display: "inline-flex",
    alignItems: "baseline",
    gap: "0.4rem",
    padding: "0.15rem 0.55rem",
    borderRadius: "0.375rem",
    border: "1px solid rgba(128, 128, 128, 0.25)",
    fontSize: "0.875rem",
    lineHeight: 1.5
  };
  const codeStyle = {
    fontFamily: "var(--font-mono, ui-monospace, monospace)",
    fontWeight: 600,
    fontSize: "0.8125rem"
  };
  const controlStyle = {
    color: "inherit",
    background: "transparent",
    border: "1px solid rgba(128, 128, 128, 0.3)",
    borderRadius: "0.5rem",
    fontSize: "0.875rem"
  };
  const renderChip = it => <span key={it.code} style={chipStyle} title={isCountries ? it.code : it.label || it.code}>
      {isCountries ? it.label || it.code : <span style={codeStyle}>{it.code}</span>}
      {!isCountries && it.label ? <span style={{
    opacity: 0.7
  }}>{it.label}</span> : null}
    </span>;
  const PREVIEW = 5;
  const collapsible = items.length > PREVIEW;
  const q = query.trim().toLowerCase();
  const filtered = q ? items.filter(it => it.code.toLowerCase().includes(q) || it.label && it.label.toLowerCase().includes(q)) : items;
  const expanded = open || q !== "";
  const visible = !collapsible ? items : expanded ? filtered : items.slice(0, PREVIEW);
  const toggle = () => {
    const next = !open;
    setOpen(next);
    if (!next) setQuery("");
  };
  return <div>
      <p>
        {name} {verb} the following {codes.length} {noun}:
      </p>

      {collapsible ? <input type="text" value={query} onChange={e => setQuery(e.target.value)} placeholder={`Filter ${noun}…`} aria-label={`Filter ${noun}`} style={{
    ...controlStyle,
    display: "block",
    width: "100%",
    maxWidth: "22rem",
    padding: "0.4rem 0.7rem",
    margin: "0 0 0.75rem"
  }} /> : null}

      <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.4rem"
  }}>
        {visible.map(renderChip)}
      </div>

      {q && filtered.length === 0 ? <p style={{
    opacity: 0.7,
    marginTop: "0.6rem"
  }}>
          No {noun} match “{query.trim()}”.
        </p> : null}
      {q && filtered.length > 0 ? <p style={{
    opacity: 0.6,
    fontSize: "0.8125rem",
    marginTop: "0.6rem"
  }}>
          Showing {filtered.length} of {items.length}.
        </p> : null}

      {collapsible && !q ? <button type="button" aria-expanded={open} onClick={toggle} style={{
    ...controlStyle,
    display: "inline-flex",
    alignItems: "center",
    gap: "0.4rem",
    padding: "0.35rem 0.75rem",
    marginTop: "0.75rem",
    cursor: "pointer"
  }}>
          <span aria-hidden="true" style={{
    display: "inline-block",
    transform: open ? "rotate(90deg)" : "none",
    transition: "transform 0.15s ease"
  }}>
            ›
          </span>
          {open ? "Show fewer" : `and ${items.length - PREVIEW} more`}
        </button> : null}
    </div>;
};

Arcus is a payment provider that enables merchants to accept SPEI (Sistema de Pagos Electrónicos Interbancarios) bank transfers in Mexico. SPEI is a push payment method where the buyer sends a payment to a CLABE number.

## Setup

Arcus does not provide self-service sign-up for a sandbox account. Please reach out to
[Arcus](https://arcusfi.com) to set up an account.

## Credentials

When setting up SPEI via Arcus in the dashboard, configure the following credentials. You can request these details via your Arcus dashboard once you are set up with an account.

* **Customer ID** - Your merchant account identifier.
* **User Name** - Your API username used to connect to the Arcus API.
* **Password** - Your API password used to connect to the Arcus API.
* **Subscription Secret** (optional) - A secret value you define to verify webhooks received from Arcus. See [Webhooks](#webhooks) below.

## Supported countries

<ConnectorRegions data={connector} kind="countries" />

## Supported currencies

<ConnectorRegions data={connector} kind="currencies" />

## Limitations

The following features are not supported by this connector:

* **Delayed capture** - Authorization and capture happen together
* **Partial capture** - Capturing a portion of the authorized amount is not supported
* **Partial refunds** - Partial refunds are not supported
* **Refunds** - Refunds are not supported
* **Void** - Canceling a transaction is not supported
* **Payment method tokenization** - Storing payment methods for future use is not supported
* **Zero auth** - Zero-dollar verification transactions are not supported
* **Network tokens** - Network tokenization is not supported
* **3-D Secure** - 3DS authentication is not applicable
* **Transaction sync** - Automatic transaction status synchronization is not supported
* **Settlement reporting** - Automatic settlement reporting is not supported

Additional push payment limitations:

* A buyer may never send a payment for various reasons, including when they copy over the CLABE number incorrectly.
* A buyer may send the wrong amount, resulting in either an over or under payment. The `capture_amount` is marked accordingly.
* A buyer may send multiple payments for the same CLABE number. The system does not handle this situation, but it is monitored and the support team is notified.

## Webhooks

The Arcus SPEI connector requires webhooks to be set up from Arcus to receive payment confirmations.

To configure webhooks:

1. Set up the connector in the dashboard, defining a secret value as the **Subscription Secret**.
2. Copy the webhook subscription URL shown in the dashboard once the connection is set up.
3. Use the Arcus [`POST /notification-subscriptions`](https://developers.arcusfi.com/docs/paas/events/operations/create-a-notification-subscription) API endpoint with the webhook URL and the `subscription_secret` you set earlier.

```json theme={"system"}
{
  "url": "https://api.example.spider.gr4vy.app/i/Xz9q_HKzTSe4CN_0lRA9jmFyY3Vc3BlaQ/R6DEA6oG3vtPI=OSU1PsRS9lmn4z-jey-SrY",
  "description": "Notifications - Sandbox",
  "subscription_secret": "my-super-secret-value",
  "subscribed_events": [
    "accounts.transactions.type.credit"
  ]
}
```

## Integration

SPEI is a push payment method. Any SPEI transaction created returns a `transaction` resource with a `status` of `processing` and a `payment_method.approval_url`. This approval URL can be displayed in a popup, iframe, or redirected to, but buyers are not redirected back to the `redirect_url` you provided when the transaction was created.

Once the buyer makes the payment to the CLABE number provided on the hosted screen, a webhook is sent from Arcus to the system, after which the status of the transaction is updated and your system is notified via a webhook.

Start by creating a new transaction with the following required fields.

<CodeGroup>
  ```csharp C# theme={"system"}
  var transaction = await client.Transactions.CreateAsync(
    transactionCreate: new TransactionCreate()
    {
      Amount = 1299,
      Currency = "MXN",
      Country = "MX",
      PaymentMethod =
        TransactionCreatePaymentMethod.CreateRedirectPaymentMethodCreate(
          new RedirectPaymentMethodCreate()
          {
            Method = "spei",
            Country = "MX",
            Currency = "MXN",
            RedirectUrl = "https://example.com/callback",
          }
        ),
    }
  );
  ```

  ```go Go theme={"system"}
  amount := int64(1299)
  currency := "MXN"
  country := "MX"
  method := components.RedirectPaymentMethodCreateMethodSpei
  redirectUrl := "https://example.com/callback"

  redirectPaymentMethodCreate := components.RedirectPaymentMethodCreate{
    Method: method,
    Country: country,
    Currency: currency,
    RedirectURL: redirectUrl,
  }
  paymentMethod := components.CreateTransactionCreatePaymentMethodRedirectPaymentMethodCreate(redirectPaymentMethodCreate)

  transactionCreate := components.TransactionCreate{
    Amount:        amount,
    Currency:      currency,
    Country:       &country,
    PaymentMethod: &paymentMethod,
  }

  transaction, err := client.Transactions.Create(ctx, transactionCreate, nil, nil, nil)
  ```

  ```java Java theme={"system"}
  CreateTransactionResponse transactionResponse = gr4vyClient.transactions().create()
    .transactionCreate(TransactionCreate.builder()
      .amount(1299L)
      .currency("MXN")
      .country("MX")
      .paymentMethod(TransactionCreatePaymentMethod.of(RedirectPaymentMethodCreate.builder()
        .method(RedirectPaymentMethodCreateMethod.SPEI)
        .country("MX")
        .currency("MXN")
        .redirectUrl("https://example.com/callback")
        .build()))
      .build())
    .call();

  Transaction transaction = transactionResponse.transaction().orElse(null);
  ```

  ```php PHP theme={"system"}
  $transactionCreate = new TransactionCreate(
    amount: 1299,
    currency: 'MXN',
    country: 'MX',
    paymentMethod: new RedirectPaymentMethodCreate(
      method: 'spei',
      country: 'MX',
      currency: 'MXN',
      redirectUrl: 'https://example.com/callback'
    )
  );
  $response = $client->transactions->create($transactionCreate);
  $transaction = $response->transaction;
  ```

  ```python Python theme={"system"}
  transaction: models.Transaction = client.transactions.create(
    amount=1299,
    currency="MXN",
    country="MX",
    payment_method={
      "method": "spei",
      "country": "MX",
      "currency": "MXN",
      "redirect_url": "https://example.com/callback",
    }
  )
  ```

  ```ts TypeScript theme={"system"}
  const transaction = await client.transactions.create({
    amount: 1299,
    currency: "MXN",
    country: "MX",
    paymentMethod: {
      method: "spei",
      country: "MX",
      currency: "MXN",
      redirectUrl: "https://example.com/callback"
    }
  })
  ```
</CodeGroup>

After the transaction is created, the API response includes a `payment_method.approval_url` and the status is set to `processing`.

```json theme={"system"}
{
  "type": "transaction",
  "id": "ea1efdd0-20f9-44d9-9b0b-0a3d71e9b625",
  "status": "processing",
  "payment_method": {
    "type": "payment-method",
    "approval_url": "https://cdn.gr4vy.com/connectors/..."
  },
  "method": "spei"
}
```

Display the `approval_url` to the buyer (in a popup, iframe, or redirect) so they can view the CLABE number and complete the payment through their bank.
