chub-dev 0.1.0 → 0.1.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1179 @@
1
+ ---
2
+ name: gateway
3
+ description: "Braintree Python SDK for payment gateway, PayPal, and subscriptions"
4
+ metadata:
5
+ languages: "python"
6
+ versions: "4.40.0"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "braintree,gateway,payments,paypal,subscriptions"
10
+ ---
11
+
12
+ # Braintree Python SDK Context
13
+
14
+ ## Golden Rule
15
+
16
+ **ALWAYS use the official Braintree Python SDK package: `braintree`**
17
+
18
+ ```bash
19
+ pip install braintree
20
+ ```
21
+
22
+ **DO NOT use:**
23
+ - Deprecated or unofficial packages
24
+ - Direct API calls without the SDK
25
+ - Outdated versions (version 3.40.0+ required for SSL certificate support)
26
+
27
+ The official package is `braintree` and is maintained by Braintree (a PayPal service).
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install braintree
33
+ ```
34
+
35
+ The SDK requires Python 3.8 or higher. The SDK also depends on the `requests` library.
36
+
37
+ ## Environment Configuration
38
+
39
+ Create a `.env` file with your Braintree credentials:
40
+
41
+ ```bash
42
+ BRAINTREE_ENVIRONMENT=Sandbox
43
+ BRAINTREE_MERCHANT_ID=your_merchant_id
44
+ BRAINTREE_PUBLIC_KEY=your_public_key
45
+ BRAINTREE_PRIVATE_KEY=your_private_key
46
+ ```
47
+
48
+ Load environment variables using `python-dotenv`:
49
+
50
+ ```bash
51
+ pip install python-dotenv
52
+ ```
53
+
54
+ ```python
55
+ from dotenv import load_dotenv
56
+ import os
57
+
58
+ load_dotenv()
59
+ ```
60
+
61
+ ## Initialization
62
+
63
+ ### Basic Gateway Setup
64
+
65
+ ```python
66
+ import braintree
67
+
68
+ gateway = braintree.BraintreeGateway(
69
+ braintree.Configuration(
70
+ braintree.Environment.Sandbox,
71
+ merchant_id="your_merchant_id",
72
+ public_key="your_public_key",
73
+ private_key="your_private_key"
74
+ )
75
+ )
76
+ ```
77
+
78
+ ### Using Environment Variables
79
+
80
+ ```python
81
+ import braintree
82
+ import os
83
+
84
+ gateway = braintree.BraintreeGateway(
85
+ braintree.Configuration(
86
+ braintree.Environment.Production if os.getenv('BRAINTREE_ENVIRONMENT') == 'Production'
87
+ else braintree.Environment.Sandbox,
88
+ merchant_id=os.getenv('BRAINTREE_MERCHANT_ID'),
89
+ public_key=os.getenv('BRAINTREE_PUBLIC_KEY'),
90
+ private_key=os.getenv('BRAINTREE_PRIVATE_KEY')
91
+ )
92
+ )
93
+ ```
94
+
95
+ ### Environment Options
96
+
97
+ ```python
98
+ # Sandbox environment (for testing)
99
+ braintree.Environment.Sandbox
100
+
101
+ # Production environment (for live transactions)
102
+ braintree.Environment.Production
103
+ ```
104
+
105
+ ## Client Token Generation
106
+
107
+ Client tokens contain authorization and configuration information for the client SDK.
108
+
109
+ ### Basic Client Token
110
+
111
+ ```python
112
+ client_token = gateway.client_token.generate()
113
+ ```
114
+
115
+ ### Client Token with Customer ID
116
+
117
+ ```python
118
+ client_token = gateway.client_token.generate({
119
+ "customer_id": "customer_123"
120
+ })
121
+ ```
122
+
123
+ ### Flask Endpoint for Client Token
124
+
125
+ ```python
126
+ from flask import Flask, jsonify
127
+
128
+ app = Flask(__name__)
129
+
130
+ @app.route("/client_token", methods=["GET"])
131
+ def get_client_token():
132
+ try:
133
+ token = gateway.client_token.generate()
134
+ return jsonify({"client_token": token})
135
+ except Exception as e:
136
+ return jsonify({"error": str(e)}), 500
137
+ ```
138
+
139
+ ### Django View for Client Token
140
+
141
+ ```python
142
+ from django.http import JsonResponse
143
+
144
+ def client_token(request):
145
+ try:
146
+ token = gateway.client_token.generate()
147
+ return JsonResponse({"client_token": token})
148
+ except Exception as e:
149
+ return JsonResponse({"error": str(e)}, status=500)
150
+ ```
151
+
152
+ ## Transactions
153
+
154
+ ### Creating a Transaction
155
+
156
+ **Basic Sale:**
157
+ ```python
158
+ result = gateway.transaction.sale({
159
+ "amount": "10.00",
160
+ "payment_method_nonce": nonce_from_the_client,
161
+ "options": {
162
+ "submit_for_settlement": True
163
+ }
164
+ })
165
+
166
+ if result.is_success:
167
+ print("Transaction ID: " + result.transaction.id)
168
+ else:
169
+ print("Error: " + result.message)
170
+ ```
171
+
172
+ ### Transaction with Device Data
173
+
174
+ ```python
175
+ result = gateway.transaction.sale({
176
+ "amount": "10.00",
177
+ "payment_method_nonce": nonce_from_the_client,
178
+ "device_data": device_data_from_the_client,
179
+ "options": {
180
+ "submit_for_settlement": True
181
+ }
182
+ })
183
+ ```
184
+
185
+ ### Transaction with Customer Information
186
+
187
+ ```python
188
+ result = gateway.transaction.sale({
189
+ "amount": "100.00",
190
+ "payment_method_nonce": nonce_from_the_client,
191
+ "customer": {
192
+ "first_name": "John",
193
+ "last_name": "Doe",
194
+ "email": "john.doe@example.com",
195
+ "phone": "312-555-1234"
196
+ },
197
+ "billing": {
198
+ "first_name": "John",
199
+ "last_name": "Doe",
200
+ "street_address": "123 Main St",
201
+ "locality": "Chicago",
202
+ "region": "IL",
203
+ "postal_code": "60614",
204
+ "country_code_alpha2": "US"
205
+ },
206
+ "shipping": {
207
+ "first_name": "John",
208
+ "last_name": "Doe",
209
+ "street_address": "123 Main St",
210
+ "locality": "Chicago",
211
+ "region": "IL",
212
+ "postal_code": "60614",
213
+ "country_code_alpha2": "US"
214
+ },
215
+ "options": {
216
+ "submit_for_settlement": True
217
+ }
218
+ })
219
+ ```
220
+
221
+ ### Transaction with Stored Payment Method
222
+
223
+ ```python
224
+ result = gateway.transaction.sale({
225
+ "amount": "10.00",
226
+ "payment_method_token": "the_token",
227
+ "options": {
228
+ "submit_for_settlement": True
229
+ }
230
+ })
231
+ ```
232
+
233
+ ### Transaction with Customer ID
234
+
235
+ ```python
236
+ result = gateway.transaction.sale({
237
+ "amount": "10.00",
238
+ "customer_id": "customer_123",
239
+ "options": {
240
+ "submit_for_settlement": True
241
+ }
242
+ })
243
+ ```
244
+
245
+ ### Flask Checkout Endpoint
246
+
247
+ ```python
248
+ from flask import Flask, request, jsonify
249
+
250
+ app = Flask(__name__)
251
+
252
+ @app.route("/checkout", methods=["POST"])
253
+ def checkout():
254
+ nonce_from_the_client = request.form.get("payment_method_nonce")
255
+ amount = request.form.get("amount")
256
+
257
+ result = gateway.transaction.sale({
258
+ "amount": amount,
259
+ "payment_method_nonce": nonce_from_the_client,
260
+ "options": {
261
+ "submit_for_settlement": True
262
+ }
263
+ })
264
+
265
+ if result.is_success:
266
+ return jsonify({
267
+ "success": True,
268
+ "transaction_id": result.transaction.id
269
+ })
270
+ else:
271
+ return jsonify({
272
+ "success": False,
273
+ "message": result.message
274
+ }), 500
275
+ ```
276
+
277
+ ### Finding a Transaction
278
+
279
+ ```python
280
+ transaction = gateway.transaction.find("the_transaction_id")
281
+ print(transaction.amount)
282
+ print(transaction.status)
283
+ print(transaction.credit_card_details.last_4)
284
+ ```
285
+
286
+ ### Refunding a Transaction
287
+
288
+ **Full Refund:**
289
+ ```python
290
+ result = gateway.transaction.refund("the_transaction_id")
291
+
292
+ if result.is_success:
293
+ print("Refund successful")
294
+ ```
295
+
296
+ **Partial Refund:**
297
+ ```python
298
+ result = gateway.transaction.refund("the_transaction_id", "10.00")
299
+
300
+ if result.is_success:
301
+ print("Partial refund successful")
302
+ ```
303
+
304
+ ### Voiding a Transaction
305
+
306
+ ```python
307
+ result = gateway.transaction.void("the_transaction_id")
308
+
309
+ if result.is_success:
310
+ print("Transaction voided")
311
+ else:
312
+ print("Error: " + result.message)
313
+ ```
314
+
315
+ ### Submitting for Settlement
316
+
317
+ **Full Amount:**
318
+ ```python
319
+ result = gateway.transaction.submit_for_settlement("transaction_id")
320
+
321
+ if result.is_success:
322
+ print("Submitted for settlement")
323
+ ```
324
+
325
+ **Partial Amount:**
326
+ ```python
327
+ result = gateway.transaction.submit_for_settlement("transaction_id", "50.00")
328
+
329
+ if result.is_success:
330
+ print("Submitted for partial settlement")
331
+ ```
332
+
333
+ ## Customer Management
334
+
335
+ ### Creating a Customer
336
+
337
+ **Basic Customer:**
338
+ ```python
339
+ result = gateway.customer.create({
340
+ "first_name": "John",
341
+ "last_name": "Doe",
342
+ "email": "john.doe@example.com",
343
+ "phone": "312-555-1234"
344
+ })
345
+
346
+ if result.is_success:
347
+ print("Customer ID: " + result.customer.id)
348
+ ```
349
+
350
+ ### Creating Customer with Company Information
351
+
352
+ ```python
353
+ result = gateway.customer.create({
354
+ "first_name": "John",
355
+ "last_name": "Doe",
356
+ "company": "Acme Corp",
357
+ "email": "john.doe@example.com",
358
+ "phone": "312-555-1234",
359
+ "fax": "614-555-5678",
360
+ "website": "www.example.com"
361
+ })
362
+ ```
363
+
364
+ ### Creating Customer with Payment Method
365
+
366
+ ```python
367
+ result = gateway.customer.create({
368
+ "first_name": "John",
369
+ "last_name": "Doe",
370
+ "email": "john.doe@example.com",
371
+ "payment_method_nonce": nonce_from_the_client
372
+ })
373
+
374
+ if result.is_success:
375
+ customer_id = result.customer.id
376
+ payment_token = result.customer.payment_methods[0].token
377
+ print(f"Customer ID: {customer_id}")
378
+ print(f"Payment Method Token: {payment_token}")
379
+ ```
380
+
381
+ ### Creating Customer with Credit Card
382
+
383
+ ```python
384
+ result = gateway.customer.create({
385
+ "first_name": "John",
386
+ "last_name": "Doe",
387
+ "email": "john.doe@example.com",
388
+ "credit_card": {
389
+ "number": "4111111111111111",
390
+ "expiration_date": "05/2025",
391
+ "cvv": "123",
392
+ "billing_address": {
393
+ "postal_code": "60614"
394
+ }
395
+ }
396
+ })
397
+
398
+ if result.is_success:
399
+ token = result.customer.payment_methods[0].token
400
+ print("Payment method token: " + token)
401
+ ```
402
+
403
+ ### Finding a Customer
404
+
405
+ ```python
406
+ customer = gateway.customer.find("the_customer_id")
407
+ print(customer.first_name)
408
+ print(customer.last_name)
409
+ print(customer.email)
410
+ print(customer.payment_methods)
411
+ ```
412
+
413
+ ### Updating a Customer
414
+
415
+ ```python
416
+ result = gateway.customer.update("the_customer_id", {
417
+ "first_name": "Jane",
418
+ "last_name": "Smith",
419
+ "email": "jane.smith@example.com"
420
+ })
421
+
422
+ if result.is_success:
423
+ print("Customer updated")
424
+ ```
425
+
426
+ ### Deleting a Customer
427
+
428
+ ```python
429
+ result = gateway.customer.delete("the_customer_id")
430
+
431
+ if result.is_success:
432
+ print("Customer deleted")
433
+ ```
434
+
435
+ ### Searching for Customers
436
+
437
+ ```python
438
+ collection = gateway.customer.search(
439
+ braintree.CustomerSearch.first_name == "John",
440
+ braintree.CustomerSearch.last_name == "Doe"
441
+ )
442
+
443
+ for customer in collection.items:
444
+ print(customer.id)
445
+ print(customer.email)
446
+ ```
447
+
448
+ ## Payment Method Management
449
+
450
+ ### Creating a Payment Method
451
+
452
+ ```python
453
+ result = gateway.payment_method.create({
454
+ "customer_id": "customer_123",
455
+ "payment_method_nonce": nonce_from_the_client
456
+ })
457
+
458
+ if result.is_success:
459
+ print("Payment Method Token: " + result.payment_method.token)
460
+ ```
461
+
462
+ ### Creating Payment Method with Billing Address
463
+
464
+ ```python
465
+ result = gateway.payment_method.create({
466
+ "customer_id": "customer_123",
467
+ "payment_method_nonce": nonce_from_the_client,
468
+ "billing_address": {
469
+ "street_address": "123 Main St",
470
+ "locality": "Chicago",
471
+ "region": "IL",
472
+ "postal_code": "60614"
473
+ }
474
+ })
475
+ ```
476
+
477
+ ### Creating Payment Method with Verification
478
+
479
+ ```python
480
+ result = gateway.payment_method.create({
481
+ "customer_id": "customer_123",
482
+ "payment_method_nonce": nonce_from_the_client,
483
+ "options": {
484
+ "verify_card": True
485
+ }
486
+ })
487
+
488
+ if result.is_success:
489
+ print("Payment method verified and created")
490
+ ```
491
+
492
+ ### Finding a Payment Method
493
+
494
+ ```python
495
+ payment_method = gateway.payment_method.find("the_token")
496
+ print(payment_method.token)
497
+ print(payment_method.card_type)
498
+ ```
499
+
500
+ ### Updating a Payment Method
501
+
502
+ **Set as Default:**
503
+ ```python
504
+ result = gateway.payment_method.update("the_token", {
505
+ "options": {
506
+ "make_default": True
507
+ }
508
+ })
509
+
510
+ if result.is_success:
511
+ print("Payment method is now default")
512
+ ```
513
+
514
+ **Update Billing Address:**
515
+ ```python
516
+ result = gateway.payment_method.update("the_token", {
517
+ "billing_address": {
518
+ "street_address": "456 Oak Ave",
519
+ "locality": "Chicago",
520
+ "region": "IL",
521
+ "postal_code": "60614",
522
+ "options": {
523
+ "update_existing": True
524
+ }
525
+ }
526
+ })
527
+ ```
528
+
529
+ ### Deleting a Payment Method
530
+
531
+ ```python
532
+ result = gateway.payment_method.delete("the_token")
533
+
534
+ if result.is_success:
535
+ print("Payment method deleted")
536
+ ```
537
+
538
+ ## Subscription Billing
539
+
540
+ ### Creating a Subscription
541
+
542
+ **Basic Subscription:**
543
+ ```python
544
+ result = gateway.subscription.create({
545
+ "payment_method_token": "the_token",
546
+ "plan_id": "plan_id"
547
+ })
548
+
549
+ if result.is_success:
550
+ print("Subscription ID: " + result.subscription.id)
551
+ ```
552
+
553
+ ### Creating Subscription with Price Override
554
+
555
+ ```python
556
+ result = gateway.subscription.create({
557
+ "payment_method_token": "the_token",
558
+ "plan_id": "plan_id",
559
+ "price": "15.00"
560
+ })
561
+ ```
562
+
563
+ ### Creating Subscription with Add-ons and Discounts
564
+
565
+ ```python
566
+ result = gateway.subscription.create({
567
+ "payment_method_token": "the_token",
568
+ "plan_id": "plan_id",
569
+ "add_ons": {
570
+ "add": [
571
+ {
572
+ "inherited_from_id": "add_on_id_1",
573
+ "amount": "10.00"
574
+ }
575
+ ]
576
+ },
577
+ "discounts": {
578
+ "add": [
579
+ {
580
+ "inherited_from_id": "discount_id_1",
581
+ "amount": "5.00"
582
+ }
583
+ ]
584
+ }
585
+ })
586
+ ```
587
+
588
+ ### Creating Subscription with Trial Period
589
+
590
+ ```python
591
+ result = gateway.subscription.create({
592
+ "payment_method_token": "the_token",
593
+ "plan_id": "plan_id",
594
+ "trial_duration": 14,
595
+ "trial_duration_unit": braintree.Subscription.TrialDurationUnit.Day,
596
+ "trial_period": True
597
+ })
598
+ ```
599
+
600
+ ### Finding a Subscription
601
+
602
+ ```python
603
+ subscription = gateway.subscription.find("subscription_id")
604
+ print(subscription.status)
605
+ print(subscription.price)
606
+ print(subscription.plan_id)
607
+ ```
608
+
609
+ ### Updating a Subscription
610
+
611
+ ```python
612
+ result = gateway.subscription.update("subscription_id", {
613
+ "price": "15.00"
614
+ })
615
+
616
+ if result.is_success:
617
+ print("Subscription updated")
618
+ ```
619
+
620
+ ### Updating Subscription Plan
621
+
622
+ ```python
623
+ result = gateway.subscription.update("subscription_id", {
624
+ "plan_id": "new_plan_id",
625
+ "price": "20.00"
626
+ })
627
+ ```
628
+
629
+ ### Canceling a Subscription
630
+
631
+ ```python
632
+ result = gateway.subscription.cancel("subscription_id")
633
+
634
+ if result.is_success:
635
+ print("Subscription canceled")
636
+ ```
637
+
638
+ ### Retrieving All Plans
639
+
640
+ ```python
641
+ plans = gateway.plan.all()
642
+
643
+ for plan in plans:
644
+ print(plan.id)
645
+ print(plan.name)
646
+ print(plan.price)
647
+ ```
648
+
649
+ ## Webhooks
650
+
651
+ ### Parsing Webhook Notifications
652
+
653
+ ```python
654
+ webhook_notification = gateway.webhook_notification.parse(
655
+ request.form["bt_signature"],
656
+ request.form["bt_payload"]
657
+ )
658
+
659
+ print(webhook_notification.kind)
660
+ print(webhook_notification.timestamp)
661
+ ```
662
+
663
+ ### Flask Webhook Endpoint
664
+
665
+ ```python
666
+ from flask import Flask, request
667
+
668
+ app = Flask(__name__)
669
+
670
+ @app.route('/webhooks', methods=['POST'])
671
+ def webhooks():
672
+ try:
673
+ webhook_notification = gateway.webhook_notification.parse(
674
+ request.form["bt_signature"],
675
+ request.form["bt_payload"]
676
+ )
677
+
678
+ # Handle different webhook types
679
+ if webhook_notification.kind == braintree.WebhookNotification.Kind.SubscriptionCanceled:
680
+ print(f"Subscription {webhook_notification.subscription.id} canceled")
681
+ elif webhook_notification.kind == braintree.WebhookNotification.Kind.SubscriptionChargedSuccessfully:
682
+ print(f"Subscription {webhook_notification.subscription.id} charged")
683
+ elif webhook_notification.kind == braintree.WebhookNotification.Kind.DisputeOpened:
684
+ print(f"Dispute opened for transaction {webhook_notification.dispute.transaction.id}")
685
+
686
+ return "", 200
687
+ except Exception as e:
688
+ print(f"Error: {e}")
689
+ return "", 400
690
+ ```
691
+
692
+ ### Django Webhook View
693
+
694
+ ```python
695
+ from django.http import HttpResponse
696
+ from django.views.decorators.csrf import csrf_exempt
697
+
698
+ @csrf_exempt
699
+ def webhook(request):
700
+ try:
701
+ webhook_notification = gateway.webhook_notification.parse(
702
+ request.POST["bt_signature"],
703
+ request.POST["bt_payload"]
704
+ )
705
+
706
+ # Handle webhook
707
+ if webhook_notification.kind == braintree.WebhookNotification.Kind.SubscriptionWentPastDue:
708
+ subscription_id = webhook_notification.subscription.id
709
+ # Handle past due subscription
710
+
711
+ return HttpResponse(status=200)
712
+ except Exception as e:
713
+ return HttpResponse(status=400)
714
+ ```
715
+
716
+ ### Handling Subscription Webhooks
717
+
718
+ ```python
719
+ webhook_notification = gateway.webhook_notification.parse(
720
+ signature,
721
+ payload
722
+ )
723
+
724
+ if webhook_notification.kind == braintree.WebhookNotification.Kind.SubscriptionWentPastDue:
725
+ subscription_id = webhook_notification.subscription.id
726
+ # Handle past due subscription
727
+ elif webhook_notification.kind == braintree.WebhookNotification.Kind.SubscriptionChargedSuccessfully:
728
+ subscription_id = webhook_notification.subscription.id
729
+ transaction_id = webhook_notification.subscription.transactions[0].id
730
+ # Handle successful charge
731
+ ```
732
+
733
+ ### Verifying Webhook Signatures
734
+
735
+ ```python
736
+ verification_string = gateway.webhook_notification.verify("challenge_string")
737
+ print(verification_string)
738
+ ```
739
+
740
+ ### Testing Webhooks
741
+
742
+ ```python
743
+ sample_notification = gateway.webhook_testing.sample_notification(
744
+ braintree.WebhookNotification.Kind.SubscriptionWentPastDue,
745
+ "subscription_id"
746
+ )
747
+
748
+ webhook_notification = gateway.webhook_notification.parse(
749
+ sample_notification['bt_signature'],
750
+ sample_notification['bt_payload']
751
+ )
752
+
753
+ print(webhook_notification.kind)
754
+ print(webhook_notification.subscription.id)
755
+ ```
756
+
757
+ ## Advanced Features
758
+
759
+ ### Searching Transactions
760
+
761
+ ```python
762
+ collection = gateway.transaction.search(
763
+ braintree.TransactionSearch.amount >= "10.00",
764
+ braintree.TransactionSearch.amount <= "100.00",
765
+ braintree.TransactionSearch.status == braintree.Transaction.Status.Settled,
766
+ braintree.TransactionSearch.created_at >= "2024-01-01"
767
+ )
768
+
769
+ for transaction in collection.items:
770
+ print(transaction.id)
771
+ print(transaction.amount)
772
+ ```
773
+
774
+ ### Transaction with Custom Fields
775
+
776
+ ```python
777
+ result = gateway.transaction.sale({
778
+ "amount": "10.00",
779
+ "payment_method_nonce": nonce_from_the_client,
780
+ "custom_fields": {
781
+ "order_id": "12345",
782
+ "user_type": "premium"
783
+ },
784
+ "options": {
785
+ "submit_for_settlement": True
786
+ }
787
+ })
788
+ ```
789
+
790
+ ### Transaction with Order ID
791
+
792
+ ```python
793
+ result = gateway.transaction.sale({
794
+ "amount": "10.00",
795
+ "payment_method_nonce": nonce_from_the_client,
796
+ "order_id": "order_123",
797
+ "options": {
798
+ "submit_for_settlement": True
799
+ }
800
+ })
801
+ ```
802
+
803
+ ### Holding in Escrow
804
+
805
+ ```python
806
+ result = gateway.transaction.sale({
807
+ "amount": "100.00",
808
+ "payment_method_nonce": nonce_from_the_client,
809
+ "service_fee_amount": "10.00",
810
+ "merchant_account_id": "submerchant_account_id",
811
+ "options": {
812
+ "submit_for_settlement": True,
813
+ "hold_in_escrow": True
814
+ }
815
+ })
816
+ ```
817
+
818
+ ### Transaction with Descriptor
819
+
820
+ ```python
821
+ result = gateway.transaction.sale({
822
+ "amount": "10.00",
823
+ "payment_method_nonce": nonce_from_the_client,
824
+ "descriptor": {
825
+ "name": "Company*Product",
826
+ "phone": "8004567890",
827
+ "url": "example.com"
828
+ },
829
+ "options": {
830
+ "submit_for_settlement": True
831
+ }
832
+ })
833
+ ```
834
+
835
+ ### Storing Payment Method on Success
836
+
837
+ ```python
838
+ result = gateway.transaction.sale({
839
+ "amount": "10.00",
840
+ "payment_method_nonce": nonce_from_the_client,
841
+ "customer_id": "customer_123",
842
+ "options": {
843
+ "submit_for_settlement": True,
844
+ "store_in_vault_on_success": True
845
+ }
846
+ })
847
+
848
+ if result.is_success:
849
+ payment_token = result.transaction.credit_card_details.token
850
+ print(f"Payment method stored with token: {payment_token}")
851
+ ```
852
+
853
+ ### Cloning a Transaction
854
+
855
+ ```python
856
+ result = gateway.transaction.clone_transaction(
857
+ "original_transaction_id",
858
+ {
859
+ "amount": "20.00",
860
+ "options": {
861
+ "submit_for_settlement": True
862
+ }
863
+ }
864
+ )
865
+
866
+ if result.is_success:
867
+ print("Cloned transaction ID: " + result.transaction.id)
868
+ ```
869
+
870
+ ### Dispute Management
871
+
872
+ **Finding a Dispute:**
873
+ ```python
874
+ dispute = gateway.dispute.find("dispute_id")
875
+ print(dispute.status)
876
+ print(dispute.amount)
877
+ print(dispute.reason)
878
+ ```
879
+
880
+ **Accept Dispute:**
881
+ ```python
882
+ result = gateway.dispute.accept("dispute_id")
883
+ ```
884
+
885
+ **Add Text Evidence:**
886
+ ```python
887
+ result = gateway.dispute.add_text_evidence("dispute_id", "evidence_text")
888
+ ```
889
+
890
+ **Finalize Dispute:**
891
+ ```python
892
+ result = gateway.dispute.finalize("dispute_id")
893
+ ```
894
+
895
+ ### Merchant Account Creation
896
+
897
+ ```python
898
+ result = gateway.merchant_account.create({
899
+ "individual": {
900
+ "first_name": "John",
901
+ "last_name": "Doe",
902
+ "email": "john.doe@example.com",
903
+ "date_of_birth": "1980-01-01",
904
+ "ssn": "123-45-6789",
905
+ "address": {
906
+ "street_address": "123 Main St",
907
+ "locality": "Chicago",
908
+ "region": "IL",
909
+ "postal_code": "60614"
910
+ }
911
+ },
912
+ "business": {
913
+ "legal_name": "John Doe Inc",
914
+ "dba_name": "JD Services",
915
+ "tax_id": "12-3456789",
916
+ "address": {
917
+ "street_address": "123 Main St",
918
+ "locality": "Chicago",
919
+ "region": "IL",
920
+ "postal_code": "60614"
921
+ }
922
+ },
923
+ "funding": {
924
+ "destination": braintree.MerchantAccount.FundingDestination.Bank,
925
+ "routing_number": "021000021",
926
+ "account_number": "1234567890"
927
+ },
928
+ "tos_accepted": True,
929
+ "master_merchant_account_id": "master_merchant_account_id"
930
+ })
931
+
932
+ if result.is_success:
933
+ print("Merchant Account ID: " + result.merchant_account.id)
934
+ ```
935
+
936
+ ## Error Handling
937
+
938
+ ### Checking Result Success
939
+
940
+ ```python
941
+ result = gateway.transaction.sale({
942
+ "amount": "10.00",
943
+ "payment_method_nonce": nonce_from_the_client,
944
+ "options": {
945
+ "submit_for_settlement": True
946
+ }
947
+ })
948
+
949
+ if result.is_success:
950
+ print("Transaction ID:", result.transaction.id)
951
+ else:
952
+ print("Error:", result.message)
953
+
954
+ # Check validation errors
955
+ for error in result.errors.deep_errors:
956
+ print("Error:", error.code)
957
+ print("Message:", error.message)
958
+ print("Attribute:", error.attribute)
959
+ ```
960
+
961
+ ### Handling Validation Errors
962
+
963
+ ```python
964
+ result = gateway.customer.create({
965
+ "email": "invalid-email"
966
+ })
967
+
968
+ if not result.is_success:
969
+ for error in result.errors.deep_errors:
970
+ print(f"Error Code: {error.code}")
971
+ print(f"Message: {error.message}")
972
+ print(f"Attribute: {error.attribute}")
973
+ ```
974
+
975
+ ### Handling Not Found Errors
976
+
977
+ ```python
978
+ try:
979
+ customer = gateway.customer.find("nonexistent_id")
980
+ except braintree.exceptions.NotFoundError:
981
+ print("Customer not found")
982
+ ```
983
+
984
+ ### Handling Authentication Errors
985
+
986
+ ```python
987
+ try:
988
+ result = gateway.transaction.sale({
989
+ "amount": "10.00",
990
+ "payment_method_nonce": nonce_from_the_client
991
+ })
992
+ except braintree.exceptions.AuthenticationError:
993
+ print("Invalid API credentials")
994
+ except braintree.exceptions.AuthorizationError:
995
+ print("Not authorized to perform this action")
996
+ ```
997
+
998
+ ## Common Patterns
999
+
1000
+ ### Complete Flask Application Example
1001
+
1002
+ ```python
1003
+ from flask import Flask, request, jsonify, render_template
1004
+ import braintree
1005
+ import os
1006
+
1007
+ app = Flask(__name__)
1008
+
1009
+ gateway = braintree.BraintreeGateway(
1010
+ braintree.Configuration(
1011
+ braintree.Environment.Sandbox,
1012
+ merchant_id=os.getenv('BRAINTREE_MERCHANT_ID'),
1013
+ public_key=os.getenv('BRAINTREE_PUBLIC_KEY'),
1014
+ private_key=os.getenv('BRAINTREE_PRIVATE_KEY')
1015
+ )
1016
+ )
1017
+
1018
+ @app.route('/')
1019
+ def index():
1020
+ return render_template('index.html')
1021
+
1022
+ @app.route('/client_token', methods=['GET'])
1023
+ def get_client_token():
1024
+ try:
1025
+ token = gateway.client_token.generate()
1026
+ return jsonify({"client_token": token})
1027
+ except Exception as e:
1028
+ return jsonify({"error": str(e)}), 500
1029
+
1030
+ @app.route('/checkout', methods=['POST'])
1031
+ def checkout():
1032
+ nonce_from_the_client = request.form.get("payment_method_nonce")
1033
+ amount = request.form.get("amount")
1034
+
1035
+ result = gateway.transaction.sale({
1036
+ "amount": amount,
1037
+ "payment_method_nonce": nonce_from_the_client,
1038
+ "options": {
1039
+ "submit_for_settlement": True
1040
+ }
1041
+ })
1042
+
1043
+ if result.is_success:
1044
+ return jsonify({
1045
+ "success": True,
1046
+ "transaction_id": result.transaction.id
1047
+ })
1048
+ else:
1049
+ return jsonify({
1050
+ "success": False,
1051
+ "message": result.message
1052
+ }), 500
1053
+
1054
+ if __name__ == '__main__':
1055
+ app.run(debug=True, port=3000)
1056
+ ```
1057
+
1058
+ ### Customer Creation with Payment Method Workflow
1059
+
1060
+ ```python
1061
+ # Step 1: Create customer
1062
+ result = gateway.customer.create({
1063
+ "first_name": "John",
1064
+ "last_name": "Doe",
1065
+ "email": "john.doe@example.com",
1066
+ "payment_method_nonce": nonce_from_the_client
1067
+ })
1068
+
1069
+ if result.is_success:
1070
+ customer_id = result.customer.id
1071
+ payment_token = result.customer.payment_methods[0].token
1072
+
1073
+ # Step 2: Create subscription
1074
+ sub_result = gateway.subscription.create({
1075
+ "payment_method_token": payment_token,
1076
+ "plan_id": "monthly_plan"
1077
+ })
1078
+
1079
+ if sub_result.is_success:
1080
+ print("Subscription ID:", sub_result.subscription.id)
1081
+ ```
1082
+
1083
+ ### Stored Payment Method Workflow
1084
+
1085
+ ```python
1086
+ # Step 1: Get customer
1087
+ customer = gateway.customer.find("customer_id")
1088
+
1089
+ # Step 2: Find default payment method
1090
+ default_payment_method = None
1091
+ for pm in customer.payment_methods:
1092
+ if pm.default:
1093
+ default_payment_method = pm
1094
+ break
1095
+
1096
+ # Step 3: Use default payment method for transaction
1097
+ if default_payment_method:
1098
+ result = gateway.transaction.sale({
1099
+ "amount": "25.00",
1100
+ "payment_method_token": default_payment_method.token,
1101
+ "options": {
1102
+ "submit_for_settlement": True
1103
+ }
1104
+ })
1105
+
1106
+ if result.is_success:
1107
+ print("Transaction ID:", result.transaction.id)
1108
+ ```
1109
+
1110
+ ### Django Integration Example
1111
+
1112
+ ```python
1113
+ # views.py
1114
+ from django.http import JsonResponse
1115
+ from django.views.decorators.csrf import csrf_exempt
1116
+ import braintree
1117
+ import os
1118
+
1119
+ gateway = braintree.BraintreeGateway(
1120
+ braintree.Configuration(
1121
+ braintree.Environment.Sandbox,
1122
+ merchant_id=os.getenv('BRAINTREE_MERCHANT_ID'),
1123
+ public_key=os.getenv('BRAINTREE_PUBLIC_KEY'),
1124
+ private_key=os.getenv('BRAINTREE_PRIVATE_KEY')
1125
+ )
1126
+ )
1127
+
1128
+ def client_token(request):
1129
+ try:
1130
+ token = gateway.client_token.generate()
1131
+ return JsonResponse({"client_token": token})
1132
+ except Exception as e:
1133
+ return JsonResponse({"error": str(e)}, status=500)
1134
+
1135
+ @csrf_exempt
1136
+ def checkout(request):
1137
+ if request.method == 'POST':
1138
+ nonce = request.POST.get('payment_method_nonce')
1139
+ amount = request.POST.get('amount')
1140
+
1141
+ result = gateway.transaction.sale({
1142
+ "amount": amount,
1143
+ "payment_method_nonce": nonce,
1144
+ "options": {
1145
+ "submit_for_settlement": True
1146
+ }
1147
+ })
1148
+
1149
+ if result.is_success:
1150
+ return JsonResponse({
1151
+ "success": True,
1152
+ "transaction_id": result.transaction.id
1153
+ })
1154
+ else:
1155
+ return JsonResponse({
1156
+ "success": False,
1157
+ "message": result.message
1158
+ }, status=500)
1159
+ ```
1160
+
1161
+ ### Subscription Renewal Handler
1162
+
1163
+ ```python
1164
+ def handle_subscription_renewal(subscription_id):
1165
+ try:
1166
+ subscription = gateway.subscription.find(subscription_id)
1167
+
1168
+ if subscription.status == braintree.Subscription.Status.PastDue:
1169
+ # Attempt to retry billing
1170
+ result = gateway.subscription.retry_charge(subscription_id)
1171
+
1172
+ if result.is_success:
1173
+ print(f"Successfully charged subscription {subscription_id}")
1174
+ else:
1175
+ print(f"Failed to charge subscription: {result.message}")
1176
+
1177
+ except braintree.exceptions.NotFoundError:
1178
+ print(f"Subscription {subscription_id} not found")
1179
+ ```