<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Neeraj Kumar</title>
    <description>The latest articles on DEV Community by Neeraj Kumar (@n_neeraj_k).</description>
    <link>https://dev.to/n_neeraj_k</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1045636%2F45027336-6014-40bd-85b9-eef6cefe54fe.jpg</url>
      <title>DEV Community: Neeraj Kumar</title>
      <link>https://dev.to/n_neeraj_k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/n_neeraj_k"/>
    <language>en</language>
    <item>
      <title>Prism: A stateless payment integration library extracted from 4 years of production</title>
      <dc:creator>Neeraj Kumar</dc:creator>
      <pubDate>Thu, 16 Apr 2026 15:40:55 +0000</pubDate>
      <link>https://dev.to/hyperswitchio/prism-a-stateless-payment-integration-library-extracted-from-4-years-of-production-555o</link>
      <guid>https://dev.to/hyperswitchio/prism-a-stateless-payment-integration-library-extracted-from-4-years-of-production-555o</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ve1j1cym6pjl1qzahrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ve1j1cym6pjl1qzahrp.png" alt="Hyperswitch Prism" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;If you have ever integrated a payment processor, you know the drill. You read through a PDF that was last updated in 2019, figure out what combination of API keys goes in which header, discover that "decline code 51" means something subtly different on this processor than the last one you dealt with, and then do it all over again when your business decides to add a second processor.&lt;/p&gt;

&lt;p&gt;We have been living in this world for years building &lt;a href="https://github.com/juspay/hyperswitch" rel="noopener noreferrer"&gt;Juspay Hyperswitch&lt;/a&gt;, an open-source and composable payments platform. At some point we had integrations for 100+ connectors. The integrations worked well — but they were locked inside our orchestrator, not usable by anyone who just needed to talk to Stripe or Adyen without adopting an entire platform.&lt;/p&gt;

&lt;p&gt;And we always felt the payment APIs are not more complicated than database drivers. It is just that the industry has not arrived at a standard (and likely never will) for payments.&lt;/p&gt;

&lt;p&gt;Hence, we decided to extract the integrations into a lightweight open interface for developers and AI agents to use, rather than recreate it every time.&lt;/p&gt;

&lt;p&gt;This post is about how we did that: unbundling those integrations into a standalone library called &lt;strong&gt;&lt;a href="https://github.com/juspay/hyperswitch-prism" rel="noopener noreferrer"&gt;Prism&lt;/a&gt;&lt;/strong&gt;, and the engineering decisions we made along the way. Some of them are genuinely interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why unbundle at all?
&lt;/h2&gt;

&lt;p&gt;The connector integrations inside Hyperswitch were not designed to be embedded in an orchestrator forever. They were always a self-contained layer: translate a unified request into a connector-specific HTTP call, make the call, translate the response back. The orchestrator was just the first thing to use them.&lt;/p&gt;

&lt;p&gt;The more we looked at it, the more it seemed wrong to keep that capability locked behind a full platform deployment. If you just need to accept payments through Stripe, you should not have to adopt an orchestrator to get a well-tested, maintained integration. And if you want to switch to Adyen later, that should be a config change, not a rewrite.&lt;/p&gt;

&lt;p&gt;So we separated the integration layer out. The result is a library with a well-defined specification — a protobuf schema covering the full payment lifecycle — that can be embedded directly in any application or deployed as a standalone service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why protobuf for the specification?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q: JSON schemas exist. OpenAPI exists. Why protobuf?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The core requirement was multi-language client generation. We needed Python developers, Java developers, TypeScript developers, and Rust developers to all be able to consume this library with first-class, type-safe APIs — without anyone hand-writing SDK code in each language. Protobuf has the most mature ecosystem for this: &lt;code&gt;prost&lt;/code&gt; for Rust, &lt;code&gt;protoc-gen-java&lt;/code&gt; for Java, &lt;code&gt;grpc_tools.protoc&lt;/code&gt; for Python, and so on. It also doubles as our gRPC interface description when the library is deployed as a server, which turned out to be a natural fit for the two deployment modes we wanted to support.&lt;/p&gt;

&lt;p&gt;The specification covers the full payment lifecycle across nine services:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PaymentService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authorize, capture, void, refund, sync — the core lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RecurringPaymentService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Charge and revoke mandates for subscriptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RefundService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieve and sync refund statuses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DisputeService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Submit evidence, defend, and accept chargebacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EventService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Process inbound webhook events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PaymentMethodService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tokenize and retrieve payment methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CustomerService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create and manage customer profiles at connectors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MerchantAuthenticationService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Access tokens, session tokens, Apple Pay / Google Pay session init&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PaymentMethodAuthenticationService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3DS pre/authenticate/post flows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Everything is strongly typed. &lt;code&gt;PaymentService.Authorize&lt;/code&gt; takes a &lt;code&gt;PaymentServiceAuthorizeRequest&lt;/code&gt; — amount, currency, payment method details, customer, metadata, capture method — and returns a &lt;code&gt;PaymentServiceAuthorizeResponse&lt;/code&gt; with a unified status enum, connector reference IDs, and structured error details. No freeform JSON blobs. No stringly-typed status fields. The spec is the contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  The implementation: Rust at the core
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q: Why Rust? Wouldn't Go or Java be simpler?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few reasons. First, we already had 50+ connector implementations in Rust from Hyperswitch, so starting there was practical. But more importantly: the library needs to be embeddable in Python, JavaScript, and Java applications without a separate process or a runtime dependency like the JVM or a Python interpreter. The only realistic way to distribute a native library that loads cleanly into all of those runtimes is as a compiled shared library — &lt;code&gt;.so&lt;/code&gt; on Linux, &lt;code&gt;.dylib&lt;/code&gt; on macOS. Rust produces exactly that, with no garbage collector pauses, no runtime to ship, and memory safety that does not require a GC.&lt;/p&gt;

&lt;p&gt;The Rust codebase is organized into a handful of internal crates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;connector-integration&lt;/code&gt;&lt;/strong&gt; — The actual connector logic: 50+ implementations translating unified domain types into connector-specific HTTP requests and parsing responses back&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;domain_types&lt;/code&gt;&lt;/strong&gt; — Shared models: &lt;code&gt;RouterDataV2&lt;/code&gt;, flow markers (&lt;code&gt;Authorize&lt;/code&gt;, &lt;code&gt;Capture&lt;/code&gt;, &lt;code&gt;Refund&lt;/code&gt;, ...), request/response data types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;grpc-api-types&lt;/code&gt;&lt;/strong&gt; — Rust types generated from the protobuf spec via &lt;code&gt;prost&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;interfaces&lt;/code&gt;&lt;/strong&gt; — The trait definitions that connector implementations must satisfy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The two-phase transformer pattern
&lt;/h3&gt;

&lt;p&gt;The single most important design decision in the Rust core is that &lt;strong&gt;the library never makes HTTP calls itself&lt;/strong&gt;. Every payment operation is split into two pure functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐    req_transformer      ┌──────────────────┐
│  Unified    │ ──────────────────────▶ │ Connector HTTP   │
│  Request    │                         │ Request          │
│  (proto)    │                         │ (URL, headers,   │
└─────────────┘                         │  body)           │
                                        └────────┬─────────┘
                                                 │  you make this call
                                                 ▼
┌─────────────┐    res_transformer      ┌──────────────────┐
│  Unified    │ ◀────────────────────── │ Connector HTTP   │
│  Response   │                         │ Response         │
│  (proto)    │                         │ (raw bytes)      │
└─────────────┘                         └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;req_transformer&lt;/code&gt; takes your unified protobuf request and returns the connector-specific HTTP request — the URL, the headers, the serialized body. You make the HTTP call however you like. &lt;code&gt;res_transformer&lt;/code&gt; takes the raw response bytes plus the original request and returns a unified protobuf response.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q: Why not just have the library make the HTTP call for you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mostly because it makes the library genuinely stateless and transport-agnostic. It does not own any connection pools. It does not have opinions about TLS configuration, proxy settings, or retry logic. When this code runs inside a Python application, the Python application's &lt;code&gt;httpx&lt;/code&gt; client handles the HTTP. When it runs inside the gRPC server, the server's client handles it. This also turns out to be quite testable — you can unit test transformers by feeding them request bytes and asserting on the resulting HTTP request structure, without standing up any network infrastructure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each flow is registered using a pair of Rust macros:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Register the request transformer for the Authorize flow&lt;/span&gt;
&lt;span class="nd"&gt;req_transformer!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;fn_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;authorize_req_transformer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;request_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentServiceAuthorizeRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;flow_marker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;resource_common_data_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentFlowData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;request_data_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentsAuthorizeData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_data_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentsResponseData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Register the response transformer for the Authorize flow&lt;/span&gt;
&lt;span class="nd"&gt;res_transformer!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;fn_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;authorize_res_transformer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;request_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentServiceAuthorizeRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentServiceAuthorizeResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;flow_marker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;resource_common_data_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentFlowData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;request_data_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentsAuthorizeData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_data_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentsResponseData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The macros generate the boilerplate: connector lookup, trait object dispatch, &lt;code&gt;RouterDataV2&lt;/code&gt; construction, serialization. A new flow means adding the connector trait implementation and one pair of macro invocations. The code generator handles everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways to use it
&lt;/h2&gt;

&lt;p&gt;We wanted the library to work both as an &lt;strong&gt;embedded SDK&lt;/strong&gt; (loaded directly into your application process) and as a &lt;strong&gt;standalone gRPC service&lt;/strong&gt; (deployed separately, called over the network). Same Rust core, same proto types, same API — two completely different deployment topologies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────┐
│                    Your Application                      │
└─────────────────────┬────────────────────────────────────┘
                      │
         ┌────────────┴────────────┐
         ▼                         ▼
 ┌──────────────┐         ┌─────────────────┐
 │   SDK Mode   │         │   gRPC Mode     │
 │  (FFI/UniFFI)│         │ (Client/Server) │
 └──────┬───────┘         └────────┬────────┘
        │                          │
        │  in-process call         │  network call
        ▼                          ▼
 ┌──────────────────────────────────────────────┐
 │              Rust Core (Prism)               │
 │  req_transformer → [HTTP] → res_transformer  │
 └──────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mode 1: The embedded SDK
&lt;/h3&gt;

&lt;p&gt;In SDK mode, the Rust core compiles into a native shared library (&lt;code&gt;.so&lt;/code&gt; / &lt;code&gt;.dylib&lt;/code&gt;) and is exposed to host languages via &lt;strong&gt;UniFFI&lt;/strong&gt; — Mozilla's framework for generating language bindings from Rust automatically. When your Python code calls &lt;code&gt;authorize_req_transformer(request_bytes, options_bytes)&lt;/code&gt;, that call crosses the FFI boundary directly into the Rust binary running in the same process.&lt;/p&gt;

&lt;p&gt;Data crosses the language boundary as serialized protobuf bytes. This is intentional — every language already has a protobuf runtime, so there is no custom serialization protocol to maintain, and the byte interface is completely language-neutral.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q: Does this mean I need to compile Rust to use the Python SDK?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For development, yes — you run &lt;code&gt;make pack&lt;/code&gt;, which builds the Rust library, runs &lt;code&gt;uniffi-bindgen&lt;/code&gt; to generate the Python bindings, and packages everything into a wheel. For production use, we ship pre-built binaries for Linux x86_64, Linux aarch64, macOS x86_64, and macOS aarch64 inside the wheel. The loader picks the right one at runtime. You install the wheel and never think about Rust again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Mode 2: The gRPC server
&lt;/h3&gt;

&lt;p&gt;In gRPC mode, the &lt;code&gt;grpc-server&lt;/code&gt; crate runs as a standalone async service built on &lt;strong&gt;Tonic&lt;/strong&gt; (Rust's async gRPC framework). It implements all nine proto services, accepts gRPC connections from any language's generated stubs, makes the connector HTTP calls internally, and returns unified proto responses over the wire.&lt;/p&gt;

&lt;p&gt;The gRPC server calls the same Rust core transformers as the FFI layer — just from a different entry point. The transformation logic is literally the same code path.&lt;/p&gt;

&lt;p&gt;Each language SDK ships both deployment modes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sdk/python/
├── src/payments/           ← FFI-based embedded SDK
│   ├── connector_client.py
│   └── _generated_service_clients.py
└── grpc-client/            ← gRPC stubs for server mode

sdk/java/
├── src/                    ← FFI-based embedded SDK (JNA + UniFFI)
└── grpc-client/            ← gRPC stubs for server mode

sdk/javascript/
├── src/payments/           ← FFI-based embedded SDK (node-ffi)
└── grpc-client/            ← gRPC stubs for server mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q: When would you actually choose gRPC over the embedded SDK?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The embedded SDK is great when you have a single-language service and want zero network overhead — serverless functions, edge deployments, or situations where adding a sidecar is painful. The gRPC server shines in polyglot environments: if your checkout service is in Java, your fraud service is in Python, and your reconciliation job is in Go, deploying one gRPC server gives all of them a shared, consistent integration layer without each one shipping a native binary.&lt;/p&gt;

&lt;p&gt;The important point is that the choice is not a migration — your &lt;code&gt;PaymentServiceAuthorizeRequest&lt;/code&gt; looks identical in both modes. You change a config flag, not your application code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;SDK (embedded)&lt;/th&gt;
&lt;th&gt;gRPC (network)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microseconds (in-process)&lt;/td&gt;
&lt;td&gt;Milliseconds (network)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Library inside your app&lt;/td&gt;
&lt;td&gt;Separate service to run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python, JS, Java/Kotlin, Rust&lt;/td&gt;
&lt;td&gt;Any language with gRPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connector HTTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your app makes the calls&lt;/td&gt;
&lt;td&gt;Server makes the calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless, edge, single-language&lt;/td&gt;
&lt;td&gt;Polyglot stacks, shared infra&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Code generation: the glue that holds it together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/juspay/hyperswitch-prism" rel="noopener noreferrer"&gt;Prism&lt;/a&gt; supports many payment flows and many SDK languages. Hand-maintaining typed client methods for each flow in each language is exactly the kind of work that introduces drift and bugs. So we don't do it.&lt;/p&gt;

&lt;p&gt;The code generator at &lt;code&gt;sdk/codegen/generate.py&lt;/code&gt; reads two sources of truth and emits all the SDK client boilerplate automatically.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q: What are the two sources of truth?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;services.proto&lt;/code&gt; compiled to a binary descriptor — this tells the generator every RPC name, its request type, its response type, and its doc comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;crates/ffi/ffi/src/services/payments.rs&lt;/code&gt; — this tells the generator which flows are actually implemented, by scanning for &lt;code&gt;req_transformer!&lt;/code&gt; invocations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The generator takes their intersection. A flow in proto but not implemented in Rust? Warning, skipped — we don't ship unimplemented APIs. A transformer in Rust with no matching proto RPC? Also a warning — the spec is the authority, not the implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Running &lt;code&gt;make generate&lt;/code&gt; produces typed client classes across all languages. For example, in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_ConnectorClientBase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentServiceAuthorizeRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PaymentServiceAuthorizeResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;PaymentService.Authorize — Authorizes a payment amount on a payment method...&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_execute_flow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authorize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_pb2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PaymentServiceAuthorizeResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in Kotlin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ConnectorConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ConnectorClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PaymentServiceAuthorizeRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RequestConfig&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PaymentServiceAuthorizeResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nf"&gt;executeFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"authorize"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toByteArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;PaymentServiceAuthorizeResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the full pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services.proto
    │
    ├── prost (Rust build.rs)      → grpc-api-types crate (Rust types)
    ├── grpc_tools.protoc          → payment_pb2.py (Python proto stubs)
    ├── protoc-gen-java            → Payment.java (Java/Kotlin proto stubs)
    ├── protoc (JS plugin)         → proto.js / proto.d.ts (JS proto stubs)
    └── protoc (binary descriptor) → services.desc
                                            │
payments.rs (transformer registrations) ───┤
                                            ▼
                                      generate.py
                                            │
        ┌───────────────────────────────────┼──────────────────────┐
        ▼                                   ▼                      ▼
_generated_ffi_flows.rs    _generated_service_clients.py    GeneratedFlows.kt
                           connector_client.pyi             _generated_connector_client_flows.ts


cargo build --features uniffi
    └── uniffi-bindgen
              ├── connector_service_ffi.py   (Python native bindings)
              ├── ConnectorServiceFfi.kt     (Kotlin/JVM native bindings)
              └── ffi.js                     (Node.js native bindings)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The practical result: add a new flow to &lt;code&gt;services.proto&lt;/code&gt;, implement the transformer pair in Rust, run &lt;code&gt;make generate&lt;/code&gt; — and every language SDK gets a typed, documented method for that flow. No one writes boilerplate by hand.&lt;/p&gt;




&lt;h2&gt;
  
  
  Walking through a real authorize call
&lt;/h2&gt;

&lt;p&gt;Let's trace what actually happens when a Python application calls &lt;code&gt;client.authorize(...)&lt;/code&gt; in SDK mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;① App builds PaymentServiceAuthorizeRequest (protobuf message)

② PaymentClient.authorize() → _execute_flow("authorize", request, ...)

③ _ConnectorClientBase._execute_flow():

   a. request.SerializeToString() → request_bytes

   b. authorize_req_transformer(request_bytes, options_bytes)
      ──── FFI boundary: Python → Rust shared library ────
      Rust: build_router_data! macro
        ├── ConnectorEnum::from("stripe")   ← look up connector
        ├── connector.get_connector_integration_v2()
        ├── proto bytes → PaymentFlowData + PaymentsAuthorizeData
        ├── construct RouterDataV2 { flow, request, auth, ... }
        └── connector.build_request(router_data) → Request { url, headers, body }
      serialize Request → FfiConnectorHttpRequest bytes
      ──── returns bytes across FFI boundary ────

   c. deserialize FfiConnectorHttpRequest → url, method, headers, body

   d. httpx AsyncClient.post(url, headers=headers, content=body)
      ← this is the actual outbound HTTP call to Stripe

   e. raw response bytes received

   f. authorize_res_transformer(response_bytes, request_bytes, options_bytes)
      ──── FFI boundary: Python → Rust shared library ────
      Rust: connector.handle_response(raw_bytes)
        ├── parse Stripe's JSON response format
        └── map → PaymentServiceAuthorizeResponse (unified proto)
      serialize → proto bytes
      ──── returns bytes across FFI boundary ────

   g. PaymentServiceAuthorizeResponse.FromString(bytes)

④ App receives unified PaymentServiceAuthorizeResponse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In gRPC mode, steps ③b through ③f happen inside the &lt;code&gt;grpc-server&lt;/code&gt; process. The app sends the protobuf request over the network and gets the protobuf response back. The connector lookup, HTTP call, and response transformation are identical — just running in a different process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we go from here — together
&lt;/h2&gt;

&lt;p&gt;We want to be upfront about what this is and what it is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; a working implementation with 60+ connectors, a protobuf specification that covers the full payment lifecycle, and SDKs in four languages. It is ready to use today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it is not:&lt;/strong&gt; a finished standard. The spec reflects our understanding of what payment integrations need to look like. That understanding is incomplete, and we know it. Payment APIs have a very long tail of edge cases — 3DS flows that differ between processors, webhook schemas that change without notice, authorization responses that technically succeeded but should be treated as soft declines. There is no team small enough to have seen all of it.&lt;/p&gt;

&lt;p&gt;That is why community ownership matters here, not as a marketing posture, but as a practical necessity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want to use it:&lt;/strong&gt; install the SDK, run &lt;code&gt;make generate&lt;/code&gt; to see what flows are available, and point it at your test credentials. When something breaks — and something will — open an issue. The more connectors and flows get exercised in real environments, the faster the rough edges get found.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want to contribute a connector:&lt;/strong&gt; implement a Rust trait in &lt;code&gt;connector-integration/&lt;/code&gt;. The FFI layer, gRPC server, and all language SDKs pick it up automatically. You do not need to write Python or JavaScript or maintain anything outside that one crate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want to contribute a flow:&lt;/strong&gt; start with a discussion on the &lt;code&gt;services.proto&lt;/code&gt; shape — that is the community contract, so it deserves a conversation before code gets written. Once there is agreement, implement the transformer pair in Rust, run &lt;code&gt;make generate&lt;/code&gt;, and every SDK gets the new method in every language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you disagree with a spec decision:&lt;/strong&gt; open a discussion. The whole point of making this community-owned is that no single team's assumptions should be baked in permanently. If you have seen payment edge cases that the current schema cannot express, that is exactly the kind of feedback that shapes a standard.&lt;/p&gt;

&lt;p&gt;The longer arc here is for &lt;code&gt;services.proto&lt;/code&gt; to evolve into something the payments community — developers, processors, orchestrators, and everyone else in the stack — maintains collectively. The same way OpenTelemetry's semantic conventions emerged from broad input, not from one company's opinions. The same way JDBC worked because it was simple enough to implement and strict enough to actually abstract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/juspay/hyperswitch-prism" rel="noopener noreferrer"&gt;GitHub: juspay/hyperswitch-prism&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>rust</category>
    </item>
    <item>
      <title>Behind the Code: Planning Hacktoberfest at Hyperswitch</title>
      <dc:creator>Neeraj Kumar</dc:creator>
      <pubDate>Thu, 26 Sep 2024 07:46:59 +0000</pubDate>
      <link>https://dev.to/n_neeraj_k/behind-the-code-planning-hacktoberfest-at-hyperswitch-31f5</link>
      <guid>https://dev.to/n_neeraj_k/behind-the-code-planning-hacktoberfest-at-hyperswitch-31f5</guid>
      <description>&lt;p&gt;Ah, October—a time for pumpkin spice lattes, cozy sweaters, and… endless lines of code? That’s right, developers around the world gear up for Hacktoberfest, an exciting opportunity to flex their coding muscles and make meaningful contributions to open-source projects. Here at &lt;a href="https://github.com/juspay/hyperswitch" rel="noopener noreferrer"&gt;Hyperswitch&lt;/a&gt;, we’ve been prepping for this year’s Hacktoberfest like it's the Olympics of coding—except, you know, with more pull requests and fewer gold medals.&lt;/p&gt;

&lt;p&gt;Let me take you behind the curtain to show you what goes into planning such a large-scale event. Spoiler alert: It involves a lot of coffee.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Stage 1: What Happened Last Year?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before diving headfirst into this year’s event, we first ask ourselves the age-old question: &lt;em&gt;What in the world happened last year?&lt;/em&gt; We dig into the data to see how many contributors we had, which trype of issues attracted the most pull requests, and, of course, whether or not we met our goals (or just managed to survive on caffeine alone).&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://github.com/juspay/hyperswitch" rel="noopener noreferrer"&gt;Hyperswitch&lt;/a&gt;, last year’s event was a hit—our community grew, and developers were happy. But we also noticed some areas where we could improve, like making the onboarding process a bit more newbie-friendly. Because, let’s be honest, jumping into open-source for the first time can feel a bit like learning to swim by being tossed into the deep end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrynf4cctaut6ivednrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrynf4cctaut6ivednrw.png" alt="Over 96 Hacktoberfest-accepted PRs last year" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Stage 2: Understanding What is Required this year&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once we’ve dusted off the past, it’s time to set this year’s targets. How many new contributors do we want? Which issue types should we shine a spotlight on? &lt;/p&gt;

&lt;p&gt;At &lt;a href="https://github.com/juspay/hyperswitch" rel="noopener noreferrer"&gt;Hyperswitch&lt;/a&gt;, we’re focusing on Dev Ex problems that could benefit 1000s of Developers. We’re also keeping a close eye on making sure first-time contributors feel welcome, because &lt;em&gt;nothing&lt;/em&gt; says “We love open-source!” like crystal-clear documentation and an easy-to-follow roadmap.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn8nanwt9177baahtn48b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn8nanwt9177baahtn48b.png" alt="Created 100 issues and we still are in process of creating more" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Stage 3: Coordinate, Coordinate, Coordinate&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Remember that thing your parents always said about teamwork? Well, Hacktoberfest planning is like that, but with a lot more Slack messages and Google Docs. I had to work closely with our design team to get the landing page live—because first impressions matter, and if it looks good, people will want to stay (kinda like a good dating profile, but for developers).&lt;/p&gt;

&lt;p&gt;And, of course, the pièce de résistance—t-shirts and stickers! Why do we pour so much love into these goodies? Simple: validation. Because who doesn’t love being rewarded for their hard work with something they can wear proudly at their next dev meetup? It’s like the developer equivalent of a superhero cape.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvhv56pbsmwyqfpp4l4y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvhv56pbsmwyqfpp4l4y.png" alt="You can checkout the amount of Google docs that is opened" width="800" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Stage 4: Fix, Fix, Fix&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Even the best-laid plans can hit a snag—or three. This stage is about fixing those last-minute hiccups, whether it’s resolving landing page bugs, updating contribution guidelines, or rewriting an issue to make it sound less like a riddle. (Nobody likes cryptic instructions, right?)&lt;/p&gt;

&lt;p&gt;For all the freshers out there, don’t be scared of this stage! Fixing small bugs, improving documentation, or just suggesting tiny tweaks is a &lt;em&gt;great&lt;/em&gt; way to get started. Trust me, you’ll feel like a wizard when your first pull request gets merged. Plus, you’ll walk away with some swag and the satisfaction of contributing to something bigger than yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why Hacktoberfest?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Why do I love Hacktoberfest? Well, besides the obvious fact that it’s a free pass to live in our IDEs for a month for Devs, it’s all about building community for me. It’s an open invitation to developers around the world to step up, contribute, and get some recognition. (Not to mention a sweet t-shirt to flex at your next virtual conference.)&lt;/p&gt;

&lt;p&gt;For us at &lt;a href="https://github.com/juspay/hyperswitch" rel="noopener noreferrer"&gt;Hyperswitch&lt;/a&gt;, Hacktoberfest is also a way to give back to the open-source community that powers so much of what we do. Whether you’re an open-source veteran or still figuring out how to fork a repo, we want you to join in. Who knows? You might just build the next Instagram!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How you can Get Involved&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Ready to jump in and make your mark during Hacktoberfest? Head over to our &lt;a href="https://hyperswitch.io/hacktoberfest" rel="noopener noreferrer"&gt;Hacktoberfest landing page&lt;/a&gt; [Don’t forget to share some feedback here], where you’ll find beginner-friendly issues, no code developments, and feature requests that need your touch.&lt;/p&gt;

&lt;p&gt;Plus, we’ve got some shiny new plugins developments [Paid “$” Contributions] and integrations in the works that are ripe for contribution. Think of it as your chance to be the hero of our open-source saga.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Wrapping It Up&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Planning Hacktoberfest is no small feat. From reflecting on last year to setting this year’s goals and coordinating every little detail, it’s a labor of love. But when the community comes together, it’s all worth it. We’re pumped to see what this year’s event brings and can’t wait to welcome developers of all experience levels to our open-source ecosystem.&lt;/p&gt;

&lt;p&gt;Join our Community: &lt;a href="https://discord.com/invite/wJZ7DVW8mm" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; | &lt;a href="https://join.slack.com/t/hyperswitch-io/shared_invite/zt-2jqxmpsbm-WXUENx022HjNEy~Ark7Orw" rel="noopener noreferrer"&gt;Slack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don’t forget to &lt;a href="https://github.com/juspay/hyperswitch" rel="noopener noreferrer"&gt;Star us on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah, I know—I’m using GPT to write this. Don’t sweat it; the robots haven’t taken over just yet. But trust me, when they do, they’ll be fully equipped to take over the world!&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>hyperswitch</category>
      <category>planning</category>
    </item>
  </channel>
</rss>
