{"id":16607534,"url":"https://github.com/davidgraeff/braintree-payment-graphql-rs","last_synced_at":"2025-10-30T20:50:45.666Z","repository":{"id":57526761,"uuid":"205034192","full_name":"davidgraeff/braintree-payment-graphql-rs","owner":"davidgraeff","description":"A Rust braintree payment client for the GraphQL API","archived":false,"fork":false,"pushed_at":"2019-09-09T11:37:33.000Z","size":187,"stargazers_count":4,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-20T13:53:35.306Z","etag":null,"topics":["braintree-payment","graphql"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidgraeff.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-28T22:29:05.000Z","updated_at":"2023-02-15T11:00:18.000Z","dependencies_parsed_at":"2022-09-07T05:30:47.529Z","dependency_job_id":null,"html_url":"https://github.com/davidgraeff/braintree-payment-graphql-rs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Fbraintree-payment-graphql-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Fbraintree-payment-graphql-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Fbraintree-payment-graphql-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Fbraintree-payment-graphql-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidgraeff","download_url":"https://codeload.github.com/davidgraeff/braintree-payment-graphql-rs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219857515,"owners_count":16556062,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["braintree-payment","graphql"],"created_at":"2024-10-12T01:23:03.205Z","updated_at":"2025-10-30T20:50:45.597Z","avatar_url":"https://github.com/davidgraeff.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Braintree Payment / GraphQL\n\n\u003cimg align=\"right\" src=\"./doc/logo.png\" /\u003e\n\n[![Build Status](https://github.com/davidgraeff/braintree-payment-graphql-rs/workflows/Integration/badge.svg)](https://github.com/davidgraeff/braintree-payment-graphql-rs/actions)\n[![](https://meritbadge.herokuapp.com/braintreepayment_graphql)](https://crates.io/crates/braintreepayment_graphql)\n[![](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)\n\nFor those unfamiliar with Braintree or payments processing, [Braintree](https://www.braintreepayments.com)'s homepage is a good place to start to learn more, along with the developer documentation which provides a good overview of the available tools and API's.\n\nThis crate allows easy access to Braintree via the GraphQL interface.\nIt offers predefined, common queries and manages connection details.\n\nThe advantage of GraphQL is the ability to write custom, specific queries\nwith only those input fields that you need and your individual selection of response fields.\n\n1. Design and test your queries in the [Braintree API Explorer](https://graphql.braintreepayments.com/explorer/).\n2. Store those graphql queries in your crates `queries` directory, for instance `queries/some_filename.graphql`.\n3. Run the \"braintree-queries\" tool via `cargo run --bin braintree-queries` in that directory.\n   The tool generates rust structs and methods to perform your queries in a type safe manner.\n   Have a look at the `examples/` directory.\n\n## Cargo features\n\n* rustls-tls: Use rustls instead of native-tls (openssl on Linux).\n  If you want to compile this crate with [MUSL](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/musl-support-for-fully-static-binaries.html),\n  this is what you want. Don't forget to disable the default features with --no-default-features.\n\n## How to get started\n\nThe first thing you want to do is create a sandbox account.\nA sandbox environment can be used to test your integration without needing to go through the full application process.\nOnce you've created an account, follow the instructions to retrieve your Merchant ID, Public Key, and Private Key and\nstore them in a `credentials.json` file. Use `credentials.json.example` as a template.\n**Never commit this file!** You should add it to your `.gitignore` file now.\n\nIn your rust program, initialize the `Braintree` object via those credentials.\n\n```rust\nuse std::error::Error;\n\nuse braintreepayment_graphql::{Braintree, Credentials};\nuse serde_json::from_str;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n    let bt = Braintree::new(Credentials::from_file(\"credentials.json\")?);\n    // OR: Avoid the IO access on start and embed the file\n    let bt = Braintree::new(from_str(include_str!(\"credentials.json\"))?);\n}\n```\n\nYou might want to adapt the http clients configuration ( [reqwest](https://docs.rs/reqwest) )\nto your needs via the [`Braintree::with_client`] constructor.\n\n### Module organisation\n\nWith the `bt` object you perform GraphQL queries (comparable to HTTP GET)\nand mutations (similar to POST, PUT).\n\nThe `bt.perform(query)` method aims to hide GraphQL details away.\nThe `query` argument refers to a *Query* name. For example \"CreateCustomer\".\n\nThis crate comes with predefined queries / mutations for customer management\nand one-time / recurring transactions. Find the GraphQL files in `queries/` and\nget an overview of available queries, parameters and return types.\n\nThe queries are organised in a hierarchical module layout, starting with `queries`.\nCustomer related queries live in the submodule `customer`, transactions in the\nsubmodule `transactions`.\n\nQuery structs reside in modules with the kebab-case formatted name of the respective\nquery (ie, a query with the name `CreateCustomer` lives in a corresponding module with\nthe name `create_customer`).\n\nFor example to bring `CreateCustomer` into scope, you would do the following\n\n```rust\nuse braintreepayment_graphql::queries::customer::create_customer::*;\n```\n\n### Create, Update, Delete Customers as well as create client tokens\n\nAll customer related operations are shown below with an example each.\n\n```rust\n#[allow(unused_imports)]\nuse braintreepayment_graphql::{Braintree};\n#[allow(unused_imports)]\nuse failure::*;\n\nfn create_customer(bt: \u0026Braintree) -\u003e Result\u003cString, failure::Error\u003e {\n    // You usually want to bring the module with the query struct and input types into scope first.\n    use braintreepayment_graphql::queries::customer::create_customer::*;\n\n    // Perform the query with bt.perform. You may create the variables structure ahead of time, or\n    // just in place like here. \n    let customer = bt\n        .perform(CreateCustomer {\n            customer: CustomerInput {\n                first_name: Some(\"first\".to_owned()),\n                last_name: Some(\"last\".to_owned()),\n                email: Some(\"test@abc.de\".to_owned()),\n                ..CustomerInput::new()\n            },\n        })? // Unwrap the response. Most of the time the interesting value is nested inside multiple strucs.\n        .create_customer.and_then(|r| r.customer).ok_or(err_msg(\"customer\"))?;\n\n    println!(\"Received customer {:?}\", customer);\n    Ok(customer.id)\n}\n```\n\nThe `bt.perform` method performs a synchronous network operation and returns with a `Result`.\nNetwork failures, invalid and bad requests but also legit errors (like \"Gateway denied\")\nresult in a returned Error.\nProper error handling is shown further down on an invalid `charge_payment` request.\n\n```rust\nfn update_customer(bt: \u0026Braintree, customer_id: \u0026str) -\u003e Result\u003c(), failure::Error\u003e {\n    use braintreepayment_graphql::queries::customer::update_customer::*;\n\n    let _ = bt.perform(UpdateCustomer {\n        cust_id: customer_id.to_owned(),\n        customer: CustomerInput {\n            first_name: Some(\"new\".to_owned()),\n            last_name: Some(\"name\".to_owned()),\n            email: Some(\"changed@abc.de\".to_owned()),\n            ..CustomerInput::new()\n        },\n    })?;\n    Ok(())\n}\n```\n\nWhile mutations (create, update, delete) have a reasonable response layout, that cannot be said\nabout queries, like the next one to retrieve a customer by ID.\n\nBraintree implements a pageable API which results in some deeply nested response structures.\nUse `unwrap_customer` to extract the customer object.\n\n```rust\nfn get_customer(bt: \u0026Braintree, customer_id: \u0026str) -\u003e Result\u003c(), failure::Error\u003e {\n    use braintreepayment_graphql::queries::{customer::get_customer::*, customer_helpers::unwrap_customer};\n\n    let customer = bt\n        .perform(\n            GetCustomer {\n                cust_id: customer_id.to_owned(),\n            },\n        )?;\n    let customer = unwrap_customer(customer).ok_or(err_msg(\"No customer found with the given ID\"))?;\n\n    assert_eq!(customer.first_name, Some(\"new\".to_owned()));\n    assert_eq!(customer.last_name, Some(\"name\".to_owned()));\n    assert_eq!(customer.email, Some(\"changed@abc.de\".to_owned()));\n    Ok(())\n}\n```\n\nA client token is necessary for the Web UI to initialize with a customer context.\n\n```rust\nfn customer_client_token(bt: \u0026Braintree, customer_id:\u0026str) -\u003e Result\u003c(), failure::Error\u003e {\n    use braintreepayment_graphql::queries::customer::customer_client_token::*;\n\n    let client_token = bt\n        .perform(CustomerClientToken {\n            cust_id: customer_id.to_owned(),\n        })?\n        .create_client_token\n        .and_then(|f| f.client_token)\n        .ok_or(err_msg(\"No token found in the response\"))?;\n\n    println!(\"{}\", client_token);\n    Ok(())\n}\n```\n\nAll Braintree GraphQL mutations allow to optionally hand a *mutation_id*.\nSuch an ID is an identifier used to reconcile requests and responses.\nSome operations like `DeleteCustomer` do not return anything\nelse but the *mutation_id*.\n\n```rust\nfn delete_customer(bt: \u0026Braintree, customer_id:\u0026str) -\u003e Result\u003c(), failure::Error\u003e {\n    use braintreepayment_graphql::queries::customer::delete_customer::*;\n\n    let delete_mut_id_orig = mutation_id();\n\n    let delete_mut_id = bt\n        .perform(DeleteCustomer {\n            cust_id: customer_id.to_owned(),\n            client_mutation_id: Some(delete_mut_id_orig.to_owned()),\n        })?\n        .delete_customer\n        .and_then(|f| f.client_mutation_id)\n        .ok_or(err_msg(\"Token\"))?;\n\n    assert_eq!(delete_mut_id_orig, delete_mut_id);\n    Ok(())\n}\n```\n\nThere are a few more, rare bits related to customers, not covered by this crates' queries.\nCheck the [Braintree API Explorer](https://graphql.braintreepayments.com/explorer/).\n\n### Transactions\n\nIf decimal accuracy is required, like in the finance sector, a proper\ndecimal number representation is necessary.\nThis library uses `rust_decimal`.\nCreate a decimal number via the macro `dec!`, ie `dec!(12.12)` or\nconvert a string representation or an integer. Use floating point numbers at your own risk!\n\nOnce you have this, you are able to create your first transaction.\nFind transaction related queries in this section.\n\n#### Charge single-use payment method\n\n```rust\nfn payment(\n    bt: \u0026Braintree,\n    payment_method_id: \u0026str,\n    amount: rust_decimal::Decimal,\n    order_id:Option\u003cString\u003e\n) -\u003e Result\u003cChargePaymentMethodChargePaymentMethodTransaction, failure::Error\u003e {\n    use braintreepayment_graphql::queries::transactions::charge_payment_method::*;\n\n    let response = bt.perform(ChargePaymentMethod {\n        payment_method_id: payment_method_id.to_owned(),\n        transaction: TransactionInput {\n            order_id,\n            purchase_order_number: Some(\"demo_id\".to_owned()),\n            ..TransactionInput::new(amount)\n        },\n        client_mutation_id: None,\n    })?.charge_payment_method\n       .and_then(|f| f.transaction)\n       .ok_or(err_msg(\"Expected a payment result\"))?;\n\n   Ok(response)\n}\n```\n\n#### Vault payment\n\nA vaulted payment response contains a new payment method ID which can be used the same like a single-use payment method ID.\n\n```rust\nfn vault(\n    bt: \u0026Braintree,\n    payment_method_id: \u0026str,\n) -\u003e Result\u003cVaultPaymentVaultPaymentMethodPaymentMethod, failure::Error\u003e {\n    use braintreepayment_graphql::queries::transactions::vault_payment::*;\n\n    let r = bt\n        .perform(VaultPayment {\n            vault_payment_input: VaultPaymentMethodInput {\n                ..VaultPaymentMethodInput::new(payment_method_id.to_owned())\n            },\n        })?\n        .vault_payment_method\n        .and_then(|f| f.payment_method)\n        .ok_or(err_msg(\"Expected a vault result\"))?;\n    Ok(r)\n}\n```\n\n#### Remove vaulted payment method\n\n```rust\nfn delete_transaction(bt: \u0026Braintree, payment_method_id: \u0026str) -\u003e Result\u003c(), failure::Error\u003e {\n    use braintreepayment_graphql::queries::transactions::delete_vaulted_payment::*;\n\n    let _ = bt.perform(DeleteVaultedPayment {\n        input: DeletePaymentMethodFromVaultInput {\n            ..DeletePaymentMethodFromVaultInput::new(payment_method_id.to_owned())\n        },\n    })?;\n\n    Ok(())\n}\n```\n\n\n#### Search for a transaction\n\n```rust\nuse braintreepayment_graphql::queries::transaction_helper::unwrap_search_result;\n\npub fn search_transaction(\n    bt: \u0026Braintree,\n    order_id: \u0026str,\n) -\u003e Result\u003cVec\u003cSearchTransactionSearchTransactionsEdgesNode\u003e, failure::Error\u003e {\n    use crate::queries::transactions::search_transaction::*;\n\n    let r = bt\n        .perform(SearchTransaction {\n            input: TransactionSearchInput {\n                order_id: Some(SearchTextInput {\n                    is: Some(order_id.to_owned()),\n                    ..SearchTextInput::new()\n                }),\n                ..TransactionSearchInput::new()\n            },\n        })?;\n\n    unwrap_search_result(r)\n}\n```\n\n\n#### Get transaction by ID\n\n```rust\nuse braintreepayment_graphql::queries::transaction_helper::unwrap_get_result;\n\npub fn get_transaction(\n    bt: \u0026Braintree,\n    transaction_id: \u0026str,\n) -\u003e Result\u003cOption\u003cGetTransactionSearchTransactionsEdgesNode\u003e, failure::Error\u003e {\n    use crate::queries::transactions::get_transaction::*;\n\n    let r = bt\n        .perform(GetTransaction {\n            transaction_id: transaction_id.to_owned(),\n        })?;\n\n    Ok(unwrap_get_result(r)?)\n}\n```\n\n### Error Handling\n\nTransactions (and other operations) may fail.\nIf the error is related to Braintree (in contrast to network errors),\nthe `braintree_error` method will return a structure with the following information:\n\n* `message` The human-readable error message. This value is not intended to be parsed and may change at any time.\n* `path` A \"path\" vector with the GraphQL query or mutation causing the error. For example [\"ChargePaymentMethod\",\"Input\",\"paymentMethodId\"]\n* `error_class` A string that represents the error class. Can be any of\n  AUTHENTICATION, AUTHORIZATION, INTERNAL, UNSUPPORTED_CLIENT, NOT_FOUND, NOT_IMPLEMENTED, RESOURCE_LIMIT, SERVICE_AVAILABILITY, VALIDATION\n\nA quote from the Braintree website:\n\n\u003e\u003e Unsuccessful transactions are a normal part of transaction processing and should not be considered exceptional. If an error occurs, you will either not receive a transaction object on the payload, or you will receive only a partial object.\n\n```rust\nfn payment_charge_fail() -\u003e Result\u003c(), failure::Error\u003e {\n    use braintreepayment_graphql::queries::transactions::charge_payment_method::*;\n    use rust_decimal_macros::*;\n\n    let bt = Braintree::new(Credentials::from_file(\"credentials.json\")?);\n\n    let payment_id = \"invalid_id\";\n\n    let response = bt.perform(ChargePaymentMethod {\n        payment_method_id: payment_id.to_owned(),\n        transaction: TransactionInput {\n            order_id: Some(\"demo_id\".to_owned()),\n            purchase_order_number: Some(\"demo_id\".to_owned()),\n            ..TransactionInput::new(dec!(12.12))\n        },\n        client_mutation_id: None,\n    });\n\n    // We have used an invalid payment method id. We expect a VALIDATION error.\n    let error = braintree_error(response.err().as_ref());\n    if !error.is_some() {\n        bail!(\"Expected error\");\n    }\n    let error = error.unwrap();\n    assert_eq!(error.error_class, \"VALIDATION\".to_owned());\n    assert_eq!(error.path, vec![\"chargePaymentMethod\".to_owned(),\"input\".to_owned(), \"paymentMethodId\".to_owned()]);\n    assert_eq!(error.message, \"Unknown or expired single-use payment method.\".to_owned());\n    Ok(())\n}\n```\n\n## Disclaimer and Limitations\n\nNote that this is an unofficial library, provided as-is, with no support whatsoever by Braintree.\nThe generator tool uses a modified branch of the graphql-client crate until 0.9 is released.\n\nMIT licensed. Pull requests are welcome.\n\nCheers,\nDavid Graeff\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidgraeff%2Fbraintree-payment-graphql-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidgraeff%2Fbraintree-payment-graphql-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidgraeff%2Fbraintree-payment-graphql-rs/lists"}