@atbas/m-mcp 0.1.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 (366) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +364 -0
  3. package/dist/builder/transaction-builder.d.ts +53 -0
  4. package/dist/builder/transaction-builder.d.ts.map +1 -0
  5. package/dist/builder/transaction-builder.js +159 -0
  6. package/dist/builder/transaction-builder.js.map +1 -0
  7. package/dist/catalogue/default-catalogue.d.ts +18 -0
  8. package/dist/catalogue/default-catalogue.d.ts.map +1 -0
  9. package/dist/catalogue/default-catalogue.js +21 -0
  10. package/dist/catalogue/default-catalogue.js.map +1 -0
  11. package/dist/catalogue/endpoints.d.ts +22 -0
  12. package/dist/catalogue/endpoints.d.ts.map +1 -0
  13. package/dist/catalogue/endpoints.js +24 -0
  14. package/dist/catalogue/endpoints.js.map +1 -0
  15. package/dist/catalogue/flows.d.ts +32 -0
  16. package/dist/catalogue/flows.d.ts.map +1 -0
  17. package/dist/catalogue/flows.js +64 -0
  18. package/dist/catalogue/flows.js.map +1 -0
  19. package/dist/catalogue/hash-options.d.ts +112 -0
  20. package/dist/catalogue/hash-options.d.ts.map +1 -0
  21. package/dist/catalogue/hash-options.js +151 -0
  22. package/dist/catalogue/hash-options.js.map +1 -0
  23. package/dist/catalogue/types.d.ts +279 -0
  24. package/dist/catalogue/types.d.ts.map +1 -0
  25. package/dist/catalogue/types.js +360 -0
  26. package/dist/catalogue/types.js.map +1 -0
  27. package/dist/codegen/generate.d.ts +14 -0
  28. package/dist/codegen/generate.d.ts.map +1 -0
  29. package/dist/codegen/generate.js +490 -0
  30. package/dist/codegen/generate.js.map +1 -0
  31. package/dist/codegen/languages/node.d.ts +3 -0
  32. package/dist/codegen/languages/node.d.ts.map +1 -0
  33. package/dist/codegen/languages/node.js +32 -0
  34. package/dist/codegen/languages/node.js.map +1 -0
  35. package/dist/codegen/languages/php.d.ts +3 -0
  36. package/dist/codegen/languages/php.d.ts.map +1 -0
  37. package/dist/codegen/languages/php.js +27 -0
  38. package/dist/codegen/languages/php.js.map +1 -0
  39. package/dist/codegen/languages/python.d.ts +3 -0
  40. package/dist/codegen/languages/python.d.ts.map +1 -0
  41. package/dist/codegen/languages/python.js +27 -0
  42. package/dist/codegen/languages/python.js.map +1 -0
  43. package/dist/codegen/renderer.d.ts +24 -0
  44. package/dist/codegen/renderer.d.ts.map +1 -0
  45. package/dist/codegen/renderer.js +59 -0
  46. package/dist/codegen/renderer.js.map +1 -0
  47. package/dist/codegen/types.d.ts +50 -0
  48. package/dist/codegen/types.d.ts.map +1 -0
  49. package/dist/codegen/types.js +5 -0
  50. package/dist/codegen/types.js.map +1 -0
  51. package/dist/config/credentials.d.ts +64 -0
  52. package/dist/config/credentials.d.ts.map +1 -0
  53. package/dist/config/credentials.js +216 -0
  54. package/dist/config/credentials.js.map +1 -0
  55. package/dist/config/environment.d.ts +23 -0
  56. package/dist/config/environment.d.ts.map +1 -0
  57. package/dist/config/environment.js +54 -0
  58. package/dist/config/environment.js.map +1 -0
  59. package/dist/core/dependencies.d.ts +43 -0
  60. package/dist/core/dependencies.d.ts.map +1 -0
  61. package/dist/core/dependencies.js +2 -0
  62. package/dist/core/dependencies.js.map +1 -0
  63. package/dist/core/registry.d.ts +33 -0
  64. package/dist/core/registry.d.ts.map +1 -0
  65. package/dist/core/registry.js +13 -0
  66. package/dist/core/registry.js.map +1 -0
  67. package/dist/core/server.d.ts +61 -0
  68. package/dist/core/server.d.ts.map +1 -0
  69. package/dist/core/server.js +90 -0
  70. package/dist/core/server.js.map +1 -0
  71. package/dist/dependencies.d.ts +28 -0
  72. package/dist/dependencies.d.ts.map +1 -0
  73. package/dist/dependencies.js +2 -0
  74. package/dist/dependencies.js.map +1 -0
  75. package/dist/diagnostics/diagnose.d.ts +43 -0
  76. package/dist/diagnostics/diagnose.d.ts.map +1 -0
  77. package/dist/diagnostics/diagnose.js +121 -0
  78. package/dist/diagnostics/diagnose.js.map +1 -0
  79. package/dist/domain/amount.d.ts +157 -0
  80. package/dist/domain/amount.d.ts.map +1 -0
  81. package/dist/domain/amount.js +275 -0
  82. package/dist/domain/amount.js.map +1 -0
  83. package/dist/domain/datetime.d.ts +95 -0
  84. package/dist/domain/datetime.d.ts.map +1 -0
  85. package/dist/domain/datetime.js +211 -0
  86. package/dist/domain/datetime.js.map +1 -0
  87. package/dist/domain/response-codes.d.ts +116 -0
  88. package/dist/domain/response-codes.d.ts.map +1 -0
  89. package/dist/domain/response-codes.js +380 -0
  90. package/dist/domain/response-codes.js.map +1 -0
  91. package/dist/domain/secure-hash.d.ts +78 -0
  92. package/dist/domain/secure-hash.d.ts.map +1 -0
  93. package/dist/domain/secure-hash.js +69 -0
  94. package/dist/domain/secure-hash.js.map +1 -0
  95. package/dist/domain/signature/jazzcash.d.ts +56 -0
  96. package/dist/domain/signature/jazzcash.d.ts.map +1 -0
  97. package/dist/domain/signature/jazzcash.js +214 -0
  98. package/dist/domain/signature/jazzcash.js.map +1 -0
  99. package/dist/domain/signature/registry.d.ts +69 -0
  100. package/dist/domain/signature/registry.d.ts.map +1 -0
  101. package/dist/domain/signature/registry.js +92 -0
  102. package/dist/domain/signature/registry.js.map +1 -0
  103. package/dist/domain/signature/scheme.d.ts +349 -0
  104. package/dist/domain/signature/scheme.d.ts.map +1 -0
  105. package/dist/domain/signature/scheme.js +398 -0
  106. package/dist/domain/signature/scheme.js.map +1 -0
  107. package/dist/domain/txn-ref.d.ts +76 -0
  108. package/dist/domain/txn-ref.d.ts.map +1 -0
  109. package/dist/domain/txn-ref.js +126 -0
  110. package/dist/domain/txn-ref.js.map +1 -0
  111. package/dist/domain/types.d.ts +45 -0
  112. package/dist/domain/types.d.ts.map +1 -0
  113. package/dist/domain/types.js +70 -0
  114. package/dist/domain/types.js.map +1 -0
  115. package/dist/errors.d.ts +84 -0
  116. package/dist/errors.d.ts.map +1 -0
  117. package/dist/errors.js +87 -0
  118. package/dist/errors.js.map +1 -0
  119. package/dist/gateway/checkout-form.d.ts +16 -0
  120. package/dist/gateway/checkout-form.d.ts.map +1 -0
  121. package/dist/gateway/checkout-form.js +103 -0
  122. package/dist/gateway/checkout-form.js.map +1 -0
  123. package/dist/gateway/checkout-output.d.ts +70 -0
  124. package/dist/gateway/checkout-output.d.ts.map +1 -0
  125. package/dist/gateway/checkout-output.js +199 -0
  126. package/dist/gateway/checkout-output.js.map +1 -0
  127. package/dist/gateway/guards.d.ts +37 -0
  128. package/dist/gateway/guards.d.ts.map +1 -0
  129. package/dist/gateway/guards.js +44 -0
  130. package/dist/gateway/guards.js.map +1 -0
  131. package/dist/gateway/http-client.d.ts +42 -0
  132. package/dist/gateway/http-client.d.ts.map +1 -0
  133. package/dist/gateway/http-client.js +153 -0
  134. package/dist/gateway/http-client.js.map +1 -0
  135. package/dist/gateway/rest-client.d.ts +42 -0
  136. package/dist/gateway/rest-client.d.ts.map +1 -0
  137. package/dist/gateway/rest-client.js +241 -0
  138. package/dist/gateway/rest-client.js.map +1 -0
  139. package/dist/gateway/transport.d.ts +31 -0
  140. package/dist/gateway/transport.d.ts.map +1 -0
  141. package/dist/gateway/transport.js +73 -0
  142. package/dist/gateway/transport.js.map +1 -0
  143. package/dist/index.d.ts +3 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +75 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/install/cli.d.ts +38 -0
  148. package/dist/install/cli.d.ts.map +1 -0
  149. package/dist/install/cli.js +274 -0
  150. package/dist/install/cli.js.map +1 -0
  151. package/dist/install/harnesses.d.ts +74 -0
  152. package/dist/install/harnesses.d.ts.map +1 -0
  153. package/dist/install/harnesses.js +263 -0
  154. package/dist/install/harnesses.js.map +1 -0
  155. package/dist/knowledge/corpus.d.ts +15 -0
  156. package/dist/knowledge/corpus.d.ts.map +1 -0
  157. package/dist/knowledge/corpus.js +129 -0
  158. package/dist/knowledge/corpus.js.map +1 -0
  159. package/dist/knowledge/search.d.ts +29 -0
  160. package/dist/knowledge/search.d.ts.map +1 -0
  161. package/dist/knowledge/search.js +181 -0
  162. package/dist/knowledge/search.js.map +1 -0
  163. package/dist/knowledge/source.d.ts +60 -0
  164. package/dist/knowledge/source.d.ts.map +1 -0
  165. package/dist/knowledge/source.js +91 -0
  166. package/dist/knowledge/source.js.map +1 -0
  167. package/dist/knowledge/types.d.ts +40 -0
  168. package/dist/knowledge/types.d.ts.map +1 -0
  169. package/dist/knowledge/types.js +19 -0
  170. package/dist/knowledge/types.js.map +1 -0
  171. package/dist/paths.d.ts +26 -0
  172. package/dist/paths.d.ts.map +1 -0
  173. package/dist/paths.js +106 -0
  174. package/dist/paths.js.map +1 -0
  175. package/dist/prompts/integration-walkthrough.d.ts +11 -0
  176. package/dist/prompts/integration-walkthrough.d.ts.map +1 -0
  177. package/dist/prompts/integration-walkthrough.js +52 -0
  178. package/dist/prompts/integration-walkthrough.js.map +1 -0
  179. package/dist/prompts/register.d.ts +4 -0
  180. package/dist/prompts/register.d.ts.map +1 -0
  181. package/dist/prompts/register.js +4 -0
  182. package/dist/prompts/register.js.map +1 -0
  183. package/dist/registry.d.ts +14 -0
  184. package/dist/registry.d.ts.map +1 -0
  185. package/dist/registry.js +2 -0
  186. package/dist/registry.js.map +1 -0
  187. package/dist/resources/knowledge-resources.d.ts +5 -0
  188. package/dist/resources/knowledge-resources.d.ts.map +1 -0
  189. package/dist/resources/knowledge-resources.js +121 -0
  190. package/dist/resources/knowledge-resources.js.map +1 -0
  191. package/dist/resources/register.d.ts +4 -0
  192. package/dist/resources/register.d.ts.map +1 -0
  193. package/dist/resources/register.js +8 -0
  194. package/dist/resources/register.js.map +1 -0
  195. package/dist/server.d.ts +40 -0
  196. package/dist/server.d.ts.map +1 -0
  197. package/dist/server.js +56 -0
  198. package/dist/server.js.map +1 -0
  199. package/dist/telemetry/logger.d.ts +31 -0
  200. package/dist/telemetry/logger.d.ts.map +1 -0
  201. package/dist/telemetry/logger.js +67 -0
  202. package/dist/telemetry/logger.js.map +1 -0
  203. package/dist/telemetry/redaction.d.ts +88 -0
  204. package/dist/telemetry/redaction.d.ts.map +1 -0
  205. package/dist/telemetry/redaction.js +238 -0
  206. package/dist/telemetry/redaction.js.map +1 -0
  207. package/dist/tools/build-transaction-parameters.d.ts +10 -0
  208. package/dist/tools/build-transaction-parameters.d.ts.map +1 -0
  209. package/dist/tools/build-transaction-parameters.js +154 -0
  210. package/dist/tools/build-transaction-parameters.js.map +1 -0
  211. package/dist/tools/call-context.d.ts +47 -0
  212. package/dist/tools/call-context.d.ts.map +1 -0
  213. package/dist/tools/call-context.js +15 -0
  214. package/dist/tools/call-context.js.map +1 -0
  215. package/dist/tools/codegen-tools.d.ts +4 -0
  216. package/dist/tools/codegen-tools.d.ts.map +1 -0
  217. package/dist/tools/codegen-tools.js +131 -0
  218. package/dist/tools/codegen-tools.js.map +1 -0
  219. package/dist/tools/credential-tools.d.ts +32 -0
  220. package/dist/tools/credential-tools.d.ts.map +1 -0
  221. package/dist/tools/credential-tools.js +65 -0
  222. package/dist/tools/credential-tools.js.map +1 -0
  223. package/dist/tools/descriptor.d.ts +52 -0
  224. package/dist/tools/descriptor.d.ts.map +1 -0
  225. package/dist/tools/descriptor.js +2 -0
  226. package/dist/tools/descriptor.js.map +1 -0
  227. package/dist/tools/diagnostic-tools.d.ts +3 -0
  228. package/dist/tools/diagnostic-tools.d.ts.map +1 -0
  229. package/dist/tools/diagnostic-tools.js +128 -0
  230. package/dist/tools/diagnostic-tools.js.map +1 -0
  231. package/dist/tools/gateway-tools.d.ts +6 -0
  232. package/dist/tools/gateway-tools.d.ts.map +1 -0
  233. package/dist/tools/gateway-tools.js +381 -0
  234. package/dist/tools/gateway-tools.js.map +1 -0
  235. package/dist/tools/hash-tools.d.ts +5 -0
  236. package/dist/tools/hash-tools.d.ts.map +1 -0
  237. package/dist/tools/hash-tools.js +238 -0
  238. package/dist/tools/hash-tools.js.map +1 -0
  239. package/dist/tools/knowledge-tools.d.ts +7 -0
  240. package/dist/tools/knowledge-tools.d.ts.map +1 -0
  241. package/dist/tools/knowledge-tools.js +247 -0
  242. package/dist/tools/knowledge-tools.js.map +1 -0
  243. package/dist/tools/onboarding-checklist.d.ts +3 -0
  244. package/dist/tools/onboarding-checklist.d.ts.map +1 -0
  245. package/dist/tools/onboarding-checklist.js +141 -0
  246. package/dist/tools/onboarding-checklist.js.map +1 -0
  247. package/dist/tools/register.d.ts +60 -0
  248. package/dist/tools/register.d.ts.map +1 -0
  249. package/dist/tools/register.js +145 -0
  250. package/dist/tools/register.js.map +1 -0
  251. package/dist/tools/tool-kit.d.ts +88 -0
  252. package/dist/tools/tool-kit.d.ts.map +1 -0
  253. package/dist/tools/tool-kit.js +297 -0
  254. package/dist/tools/tool-kit.js.map +1 -0
  255. package/dist/validation/payload-validator.d.ts +29 -0
  256. package/dist/validation/payload-validator.d.ts.map +1 -0
  257. package/dist/validation/payload-validator.js +79 -0
  258. package/dist/validation/payload-validator.js.map +1 -0
  259. package/dist/validation/rules/amount.d.ts +3 -0
  260. package/dist/validation/rules/amount.d.ts.map +1 -0
  261. package/dist/validation/rules/amount.js +77 -0
  262. package/dist/validation/rules/amount.js.map +1 -0
  263. package/dist/validation/rules/bill-reference.d.ts +10 -0
  264. package/dist/validation/rules/bill-reference.d.ts.map +1 -0
  265. package/dist/validation/rules/bill-reference.js +34 -0
  266. package/dist/validation/rules/bill-reference.js.map +1 -0
  267. package/dist/validation/rules/cnic.d.ts +3 -0
  268. package/dist/validation/rules/cnic.d.ts.map +1 -0
  269. package/dist/validation/rules/cnic.js +42 -0
  270. package/dist/validation/rules/cnic.js.map +1 -0
  271. package/dist/validation/rules/constants.d.ts +9 -0
  272. package/dist/validation/rules/constants.d.ts.map +1 -0
  273. package/dist/validation/rules/constants.js +28 -0
  274. package/dist/validation/rules/constants.js.map +1 -0
  275. package/dist/validation/rules/constraints.d.ts +3 -0
  276. package/dist/validation/rules/constraints.d.ts.map +1 -0
  277. package/dist/validation/rules/constraints.js +113 -0
  278. package/dist/validation/rules/constraints.js.map +1 -0
  279. package/dist/validation/rules/datetime.d.ts +3 -0
  280. package/dist/validation/rules/datetime.d.ts.map +1 -0
  281. package/dist/validation/rules/datetime.js +49 -0
  282. package/dist/validation/rules/datetime.js.map +1 -0
  283. package/dist/validation/rules/empty-string.d.ts +13 -0
  284. package/dist/validation/rules/empty-string.d.ts.map +1 -0
  285. package/dist/validation/rules/empty-string.js +32 -0
  286. package/dist/validation/rules/empty-string.js.map +1 -0
  287. package/dist/validation/rules/expiry.d.ts +3 -0
  288. package/dist/validation/rules/expiry.d.ts.map +1 -0
  289. package/dist/validation/rules/expiry.js +52 -0
  290. package/dist/validation/rules/expiry.js.map +1 -0
  291. package/dist/validation/rules/msisdn.d.ts +3 -0
  292. package/dist/validation/rules/msisdn.d.ts.map +1 -0
  293. package/dist/validation/rules/msisdn.js +45 -0
  294. package/dist/validation/rules/msisdn.js.map +1 -0
  295. package/dist/validation/rules/presence.d.ts +3 -0
  296. package/dist/validation/rules/presence.d.ts.map +1 -0
  297. package/dist/validation/rules/presence.js +90 -0
  298. package/dist/validation/rules/presence.js.map +1 -0
  299. package/dist/validation/rules/return-url.d.ts +14 -0
  300. package/dist/validation/rules/return-url.d.ts.map +1 -0
  301. package/dist/validation/rules/return-url.js +58 -0
  302. package/dist/validation/rules/return-url.js.map +1 -0
  303. package/dist/validation/rules/tool-argument.d.ts +16 -0
  304. package/dist/validation/rules/tool-argument.d.ts.map +1 -0
  305. package/dist/validation/rules/tool-argument.js +38 -0
  306. package/dist/validation/rules/tool-argument.js.map +1 -0
  307. package/dist/validation/rules/txn-ref.d.ts +3 -0
  308. package/dist/validation/rules/txn-ref.d.ts.map +1 -0
  309. package/dist/validation/rules/txn-ref.js +47 -0
  310. package/dist/validation/rules/txn-ref.js.map +1 -0
  311. package/dist/validation/rules/unknown-fields.d.ts +10 -0
  312. package/dist/validation/rules/unknown-fields.d.ts.map +1 -0
  313. package/dist/validation/rules/unknown-fields.js +29 -0
  314. package/dist/validation/rules/unknown-fields.js.map +1 -0
  315. package/dist/validation/types.d.ts +36 -0
  316. package/dist/validation/types.d.ts.map +1 -0
  317. package/dist/validation/types.js +12 -0
  318. package/dist/validation/types.js.map +1 -0
  319. package/dist/version.d.ts +23 -0
  320. package/dist/version.d.ts.map +1 -0
  321. package/dist/version.js +23 -0
  322. package/dist/version.js.map +1 -0
  323. package/knowledge/diagnostics/symptoms.json +321 -0
  324. package/knowledge/docs/bnpl-guide.md +77 -0
  325. package/knowledge/docs/card-page-redirection.md +67 -0
  326. package/knowledge/docs/card-refund.md +91 -0
  327. package/knowledge/docs/hmac-sha256-calculation.md +72 -0
  328. package/knowledge/docs/index.json +189 -0
  329. package/knowledge/docs/ipn-implementation.md +89 -0
  330. package/knowledge/docs/mwallet-recurring.md +117 -0
  331. package/knowledge/docs/mwallet-refund.md +65 -0
  332. package/knowledge/docs/mwallet-rest-v1-1.md +126 -0
  333. package/knowledge/docs/mwallet-rest-v2-0-cnic.md +113 -0
  334. package/knowledge/docs/portal-guide.md +45 -0
  335. package/knowledge/docs/shopify-guide.md +74 -0
  336. package/knowledge/docs/status-inquiry-2025-26.md +32 -0
  337. package/knowledge/docs/status-inquiry.md +111 -0
  338. package/knowledge/docs/wordpress-guide.md +49 -0
  339. package/knowledge/flow.schema.json +390 -0
  340. package/knowledge/flows/bnpl-page-redirection-v1.1.json +211 -0
  341. package/knowledge/flows/card-page-redirection-v1.1.json +211 -0
  342. package/knowledge/flows/mwallet-rest-v1.1.json +187 -0
  343. package/knowledge/flows/mwallet-rest-v2.0-cnic.json +197 -0
  344. package/knowledge/flows/mwallet-token-pay-v4.0.json +130 -0
  345. package/knowledge/flows/refund-bnpl.json +105 -0
  346. package/knowledge/flows/refund-card-v1.1.json +112 -0
  347. package/knowledge/flows/refund-card-v2.0.json +121 -0
  348. package/knowledge/flows/refund-mwallet-v1.1.json +106 -0
  349. package/knowledge/flows/status-inquiry-v1.1.json +102 -0
  350. package/knowledge/flows/status-inquiry-v2.0.json +103 -0
  351. package/knowledge/flows/token-delete-v1.json +85 -0
  352. package/knowledge/flows/token-inquiry-v1.json +85 -0
  353. package/knowledge/flows/wallet-linking-portal.json +103 -0
  354. package/knowledge/templates/node/checkout-redirect.tmpl +166 -0
  355. package/knowledge/templates/node/ipn-handler.tmpl +108 -0
  356. package/knowledge/templates/node/rest-client.tmpl +197 -0
  357. package/knowledge/templates/node/wallet-linking.tmpl +147 -0
  358. package/knowledge/templates/php/checkout-redirect.tmpl +149 -0
  359. package/knowledge/templates/php/ipn-handler.tmpl +105 -0
  360. package/knowledge/templates/php/rest-client.tmpl +191 -0
  361. package/knowledge/templates/php/wallet-linking.tmpl +137 -0
  362. package/knowledge/templates/python/checkout-redirect.tmpl +148 -0
  363. package/knowledge/templates/python/ipn-handler.tmpl +110 -0
  364. package/knowledge/templates/python/rest-client.tmpl +179 -0
  365. package/knowledge/templates/python/wallet-linking.tmpl +133 -0
  366. package/package.json +84 -0
@@ -0,0 +1,148 @@
1
+ """JazzCash — {{FLOW_TITLE}}
2
+
3
+ Generated by the JazzCash MCP server from {{FLOW_ID}} ({{CORPUS_VERSION}} guides).
4
+ Sources: {{DOC_SOURCES}}
5
+
6
+ Builds a signed, auto-submitting form that redirects the customer to the
7
+ JazzCash hosted payment page.
8
+
9
+ SAFETY NOTES
10
+ - Sandbox and production use the SAME URL. The environment is decided by the
11
+ credentials alone.
12
+ - The Integrity Salt is the HMAC key and must never reach the browser. This
13
+ module runs server-side and emits only the finished hash.
14
+ - pp_ReturnURL must be byte-identical to the URL registered in the portal.
15
+
16
+ Requires Python 3.9 or later (zoneinfo).
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import hashlib
22
+ import hmac
23
+ import html
24
+ import os
25
+ import secrets
26
+ import string
27
+ from datetime import datetime, timedelta
28
+ from decimal import Decimal, ROUND_DOWN
29
+ from typing import Any, Mapping
30
+ from zoneinfo import ZoneInfo
31
+
32
+ JAZZCASH_POST_URL = '{{ENDPOINT}}'
33
+ PAKISTAN = ZoneInfo('Asia/Karachi')
34
+
35
+ # pp_TxnRefNo is annotated [20AN] in the guides: at most 20 letters and digits.
36
+ MAX_REFERENCE_LENGTH = 20
37
+ REFERENCE_ALPHABET = string.ascii_letters + string.digits
38
+
39
+
40
+ def txn_date_time(moment: datetime | None = None) -> str:
41
+ return (moment or datetime.now(PAKISTAN)).astimezone(PAKISTAN).strftime('%Y%m%d%H%M%S')
42
+
43
+
44
+ def generate_reference(prefix: str, moment: datetime | None = None) -> str:
45
+ """Reference that stays unique inside a single millisecond.
46
+
47
+ The clock alone is not enough: two orders placed in the same millisecond
48
+ produce the same reference, and the gateway rejects the second as a
49
+ duplicate. The random suffix fills whatever the prefix and the timestamp
50
+ leave of the 20-character budget, drawn from `secrets`, not `random`.
51
+ """
52
+ stamp = txn_date_time(moment)
53
+ room = MAX_REFERENCE_LENGTH - len(prefix) - len(stamp)
54
+
55
+ if room < 1:
56
+ raise ValueError(
57
+ f'Reference prefix {prefix!r} leaves no room inside the {MAX_REFERENCE_LENGTH}-character limit.'
58
+ )
59
+
60
+ suffix = ''.join(secrets.choice(REFERENCE_ALPHABET) for _ in range(room))
61
+
62
+ return f'{prefix}{stamp}{suffix}'
63
+
64
+
65
+ def to_minor_units(major_amount: str) -> str:
66
+ """Convert rupees to minor units. Decimal, never float."""
67
+ value = Decimal(str(major_amount))
68
+ if value <= 0:
69
+ raise ValueError('Amount must be greater than zero.')
70
+ if value != value.quantize(Decimal('0.01'), rounding=ROUND_DOWN):
71
+ raise ValueError('Amount must have at most two decimal places.')
72
+ return str(int(value * 100))
73
+
74
+
75
+ def secure_hash(parameters: Mapping[str, Any], integrity_salt: str) -> str:
76
+ values = [
77
+ str(parameters[name])
78
+ for name in sorted(parameters)
79
+ if name != 'pp_SecureHash' and parameters[name] is not None and str(parameters[name]) != ''
80
+ ]
81
+
82
+ message = integrity_salt if not values else integrity_salt + '&' + '&'.join(values)
83
+
84
+ return hmac.new(integrity_salt.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()
85
+
86
+
87
+ def build_checkout_parameters(amount_major: str, bill_reference: str, description: str) -> dict[str, str]:
88
+ """Build the signed parameter set for one order."""
89
+ merchant_id = os.environ.get('JAZZCASH_MERCHANT_ID', '')
90
+ password = os.environ.get('JAZZCASH_PASSWORD', '')
91
+ integrity_salt = os.environ.get('JAZZCASH_INTEGRITY_SALT', '')
92
+ return_url = os.environ.get('JAZZCASH_RETURN_URL', '')
93
+
94
+ if not merchant_id or not password or not integrity_salt or not return_url:
95
+ raise RuntimeError('JazzCash credentials are not configured.')
96
+
97
+ now = datetime.now(PAKISTAN)
98
+
99
+ parameters: dict[str, str] = {
100
+ {{PARAMS}}
101
+ }
102
+ parameters['pp_MerchantID'] = merchant_id
103
+ parameters['pp_Password'] = password
104
+ parameters['pp_ReturnURL'] = return_url
105
+ # Unique per transaction. Replace TRN with your own three-letter merchant
106
+ # prefix, as the guides recommend.
107
+ parameters['pp_TxnRefNo'] = generate_reference('TRN', now)
108
+ parameters['pp_Amount'] = to_minor_units(amount_major)
109
+ parameters['pp_BillReference'] = bill_reference
110
+ parameters['pp_Description'] = description
111
+ parameters['pp_TxnDateTime'] = txn_date_time(now)
112
+ parameters['pp_TxnExpiryDateTime'] = txn_date_time(now + timedelta(days=1))
113
+
114
+ parameters['pp_SecureHash'] = secure_hash(parameters, integrity_salt)
115
+
116
+ return parameters
117
+
118
+
119
+ def render_checkout_form(parameters: Mapping[str, str]) -> str:
120
+ """Render an auto-submitting HTML form. Every value is HTML-escaped."""
121
+ inputs = '\n'.join(
122
+ f' <input type="hidden" name="{html.escape(name, quote=True)}" '
123
+ f'value="{html.escape(str(value), quote=True)}">'
124
+ for name, value in parameters.items()
125
+ )
126
+
127
+ return (
128
+ '<!DOCTYPE html>\n'
129
+ '<html lang="en">\n'
130
+ '<head><meta charset="UTF-8"><title>Redirecting to JazzCash…</title></head>\n'
131
+ '<body>\n'
132
+ ' <p>Redirecting you to the JazzCash secure payment page…</p>\n'
133
+ f' <form id="jazzcash" method="post" action="{html.escape(JAZZCASH_POST_URL, quote=True)}">\n'
134
+ f'{inputs}\n'
135
+ ' <noscript><button type="submit">Continue</button></noscript>\n'
136
+ ' </form>\n'
137
+ " <script>document.getElementById('jazzcash').submit();</script>\n"
138
+ '</body>\n'
139
+ '</html>'
140
+ )
141
+
142
+
143
+ # Example wiring with Flask:
144
+ #
145
+ # @app.post('/checkout')
146
+ # def checkout():
147
+ # parameters = build_checkout_parameters('1.00', 'billref001', 'Order 1024')
148
+ # return render_checkout_form(parameters)
@@ -0,0 +1,110 @@
1
+ """JazzCash IPN (Instant Payment Notification) listener — {{FRAMEWORK}}
2
+
3
+ Generated by the JazzCash MCP server ({{CORPUS_VERSION}} guides).
4
+ Sources: {{DOC_SOURCES}}
5
+
6
+ Register this URL under Integration > Credentials in the merchant portal.
7
+
8
+ CONTRACT
9
+ - Respond within 60 seconds. JazzCash retries twice on failure or timeout.
10
+ - Therefore this handler MUST be idempotent: the same notification can
11
+ legitimately arrive three times.
12
+ - This URL is public. Anyone can post to it. VERIFY THE HASH FIRST.
13
+ - In an inbound IPN the payment outcome is in pp_ResponseCode: 121 succeeded,
14
+ 199/999 failed. (In a Status Inquiry response it is pp_PaymentResponseCode.)
15
+
16
+ Requires `flask`.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import hashlib
22
+ import hmac
23
+ import logging
24
+ import os
25
+ from typing import Any, Mapping
26
+
27
+ from flask import Flask, jsonify, request
28
+
29
+ app = Flask(__name__)
30
+ logger = logging.getLogger(__name__)
31
+
32
+ INTEGRITY_SALT = os.environ.get('JAZZCASH_INTEGRITY_SALT', '')
33
+ if not INTEGRITY_SALT:
34
+ raise RuntimeError('JAZZCASH_INTEGRITY_SALT is not configured.')
35
+
36
+
37
+ def secure_hash(parameters: Mapping[str, Any], integrity_salt: str) -> str:
38
+ values = [
39
+ str(parameters[name])
40
+ for name in sorted(parameters)
41
+ if name not in ('pp_SecureHash', 'SecureHash')
42
+ # The gateway sends JSON null in IPN payloads even though merchants are
43
+ # told never to send null. Treat None as empty when hashing.
44
+ and parameters[name] is not None
45
+ and str(parameters[name]) != ''
46
+ ]
47
+
48
+ message = integrity_salt if not values else integrity_salt + '&' + '&'.join(values)
49
+
50
+ return hmac.new(integrity_salt.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()
51
+
52
+
53
+ def acknowledgement(message: str) -> dict[str, str]:
54
+ body = {'pp_ResponseCode': '000', 'pp_ResponseMessage': message}
55
+ body['pp_SecureHash'] = secure_hash(body, INTEGRITY_SALT)
56
+ return body
57
+
58
+
59
+ @app.post('{{IPN_PATH}}')
60
+ def jazzcash_ipn(): # type: ignore[no-untyped-def]
61
+ notification = request.get_json(silent=True)
62
+
63
+ if not isinstance(notification, dict):
64
+ return jsonify({'pp_ResponseCode': '400', 'pp_ResponseMessage': 'Malformed body'}), 400
65
+
66
+ # 1. Verify the hash BEFORE anything else.
67
+ supplied = str(notification.get('pp_SecureHash', ''))
68
+ recomputed = secure_hash(notification, INTEGRITY_SALT)
69
+
70
+ if not supplied or not hmac.compare_digest(recomputed.lower(), supplied.strip().lower()):
71
+ logger.error('JazzCash IPN rejected: secure hash did not verify.')
72
+ return jsonify({'pp_ResponseCode': '400', 'pp_ResponseMessage': 'Invalid secure hash'}), 400
73
+
74
+ txn_ref_no = str(notification.get('pp_TxnRefNo', ''))
75
+ response_code = str(notification.get('pp_ResponseCode', ''))
76
+
77
+ # 2. Look up the order. Replace with your own persistence layer.
78
+ # order = find_order_by_reference(txn_ref_no)
79
+ # if order is None:
80
+ # return jsonify(acknowledgement('IPN received successfully'))
81
+
82
+ # 3. Idempotency: a retry must not fulfil the order twice.
83
+ # if order.is_paid:
84
+ # return jsonify(acknowledgement('IPN received successfully'))
85
+
86
+ # 4. Confirm the amount matches what you recorded, so a replayed or altered
87
+ # notification cannot mark a large order paid from a small transaction.
88
+ # if str(order.amount_minor_units) != str(notification.get('pp_Amount', '')):
89
+ # logger.error('JazzCash IPN amount mismatch for %s', txn_ref_no)
90
+ # return jsonify(acknowledgement('IPN received successfully'))
91
+
92
+ # 5. Interpret the outcome.
93
+ if response_code == '121':
94
+ # Paid. Mark the order paid, then do slow work asynchronously.
95
+ # mark_order_paid(txn_ref_no, notification)
96
+ pass
97
+ elif response_code in ('199', '999'):
98
+ # Failed. Do not release goods.
99
+ # mark_order_failed(txn_ref_no, notification)
100
+ pass
101
+ else:
102
+ # Undocumented code: unresolved, NOT failed. Reconcile with Status Inquiry.
103
+ logger.error('JazzCash IPN carried undocumented code %s for %s', response_code, txn_ref_no)
104
+
105
+ # 6. Acknowledge well inside the 60-second window.
106
+ return jsonify(acknowledgement('IPN received successfully'))
107
+
108
+
109
+ if __name__ == '__main__':
110
+ app.run(port=int(os.environ.get('PORT', '3000')))
@@ -0,0 +1,179 @@
1
+ """JazzCash — {{FLOW_TITLE}}
2
+
3
+ Generated by the JazzCash MCP server from {{FLOW_ID}} ({{CORPUS_VERSION}} guides).
4
+ Sources: {{DOC_SOURCES}}
5
+
6
+ SAFETY NOTES
7
+ - Sandbox and production use the SAME URL ({{ENDPOINT}}). The environment is
8
+ decided entirely by the credentials. Confirm which are loaded before running.
9
+ - Moves money: {{MOVES_MONEY}}.
10
+
11
+ Requires Python 3.9 or later (zoneinfo) and `requests`.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import hashlib
17
+ import hmac
18
+ import os
19
+ import secrets
20
+ import string
21
+ from datetime import datetime, timedelta
22
+ from decimal import Decimal, ROUND_DOWN
23
+ from typing import Any, Mapping
24
+ from zoneinfo import ZoneInfo
25
+
26
+ import requests
27
+
28
+ JAZZCASH_ENDPOINT = '{{ENDPOINT}}'
29
+ TIMEOUT_SECONDS = 30
30
+ PAKISTAN = ZoneInfo('Asia/Karachi')
31
+
32
+ # pp_TxnRefNo and pp_RequestID are annotated [20AN] in the guides: at most 20
33
+ # letters and digits.
34
+ MAX_REFERENCE_LENGTH = 20
35
+ REFERENCE_ALPHABET = string.ascii_letters + string.digits
36
+
37
+
38
+ def txn_date_time(moment: datetime | None = None) -> str:
39
+ """Format an instant as YYYYMMDDHHMMSS in Pakistan Standard Time.
40
+
41
+ An explicit time zone is used so the stamp is correct regardless of the host
42
+ clock: a server in UTC and one in New York must produce the same value.
43
+ """
44
+ return (moment or datetime.now(PAKISTAN)).astimezone(PAKISTAN).strftime('%Y%m%d%H%M%S')
45
+
46
+
47
+ def txn_expiry_date_time(moment: datetime | None = None) -> str:
48
+ """Expiry stamp, one day after the transaction, as the guides require."""
49
+ return txn_date_time((moment or datetime.now(PAKISTAN)) + timedelta(days=1))
50
+
51
+
52
+ def to_minor_units(major_amount: str) -> str:
53
+ """Convert rupees to the minor units the gateway expects.
54
+
55
+ Decimal, never float: binary floating point cannot represent money exactly.
56
+ """
57
+ value = Decimal(str(major_amount))
58
+ if value <= 0:
59
+ raise ValueError('Amount must be greater than zero.')
60
+ if value != value.quantize(Decimal('0.01'), rounding=ROUND_DOWN):
61
+ raise ValueError('Amount must have at most two decimal places.')
62
+ return str(int(value * 100))
63
+
64
+
65
+ def generate_reference(prefix: str = 'T', moment: datetime | None = None) -> str:
66
+ """Reference that stays unique inside a single millisecond.
67
+
68
+ The clock alone is not enough: two orders placed in the same millisecond
69
+ produce the same reference, and the gateway rejects the second as a
70
+ duplicate. The random suffix fills whatever the prefix and the timestamp
71
+ leave of the 20-character budget, drawn from `secrets`, not `random`.
72
+ """
73
+ stamp = txn_date_time(moment)
74
+ room = MAX_REFERENCE_LENGTH - len(prefix) - len(stamp)
75
+
76
+ if room < 1:
77
+ raise ValueError(
78
+ f'Reference prefix {prefix!r} leaves no room inside the {MAX_REFERENCE_LENGTH}-character limit.'
79
+ )
80
+
81
+ suffix = ''.join(secrets.choice(REFERENCE_ALPHABET) for _ in range(room))
82
+
83
+ return f'{prefix}{stamp}{suffix}'
84
+
85
+
86
+ def secure_hash(parameters: Mapping[str, Any], integrity_salt: str) -> str:
87
+ """Build pp_SecureHash.
88
+
89
+ Sort by parameter NAME ascending, join the non-empty VALUES with '&',
90
+ prepend the Integrity Salt, then HMAC-SHA256 with the salt as the key.
91
+ """
92
+ values = [
93
+ str(parameters[name])
94
+ for name in sorted(parameters)
95
+ if name not in ('pp_SecureHash', 'SecureHash')
96
+ and parameters[name] is not None
97
+ and str(parameters[name]) != ''
98
+ ]
99
+
100
+ message = integrity_salt if not values else integrity_salt + '&' + '&'.join(values)
101
+
102
+ return hmac.new(integrity_salt.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()
103
+
104
+
105
+ def hash_matches(left: str, right: str) -> bool:
106
+ """Constant-time, case-insensitive comparison.
107
+
108
+ JazzCash returns uppercase hex; hexdigest() produces lowercase. Same digest.
109
+ """
110
+ return hmac.compare_digest(str(left).strip().lower(), str(right).strip().lower())
111
+
112
+
113
+ def _credentials() -> tuple[str, str, str]:
114
+ merchant_id = os.environ.get('JAZZCASH_MERCHANT_ID', '')
115
+ password = os.environ.get('JAZZCASH_PASSWORD', '')
116
+ integrity_salt = os.environ.get('JAZZCASH_INTEGRITY_SALT', '')
117
+
118
+ if not merchant_id or not password or not integrity_salt:
119
+ raise RuntimeError('JazzCash credentials are not configured.')
120
+
121
+ return merchant_id, password, integrity_salt
122
+
123
+
124
+ def run(**overrides: str) -> dict[str, Any]:
125
+ """Execute {{FLOW_ID}}.
126
+
127
+ Every value marked TODO must be supplied by your application, either by
128
+ editing the literal below or by passing it as a keyword argument. pp_Amount
129
+ is in MINOR units: pass `to_minor_units('100.00')` rather than a rupee
130
+ float, so the conversion is exact.
131
+ """
132
+ merchant_id, password, integrity_salt = _credentials()
133
+
134
+ parameters: dict[str, Any] = {
135
+ {{PARAMS}}
136
+ }
137
+ parameters.update(overrides)
138
+ parameters['pp_MerchantID'] = merchant_id
139
+ parameters['pp_Password'] = password
140
+ {{RUNTIME_OVERRIDES}}
141
+
142
+ # Hash LAST, from exactly the mapping about to be transmitted.
143
+ parameters['pp_SecureHash'] = secure_hash(parameters, integrity_salt)
144
+
145
+ # Never log `parameters`: it carries the password.
146
+ response = requests.post(
147
+ JAZZCASH_ENDPOINT,
148
+ json=parameters,
149
+ headers={'Content-Type': 'application/json', 'Accept': 'application/json'},
150
+ timeout=TIMEOUT_SECONDS,
151
+ )
152
+
153
+ try:
154
+ result: dict[str, Any] = response.json()
155
+ except ValueError as error:
156
+ raise RuntimeError(f'JazzCash returned a non-JSON body with HTTP {response.status_code}.') from error
157
+
158
+ if result.get('{{SUCCESS_FIELD}}') == '{{SUCCESS_VALUE}}':
159
+ {{SETTLEMENT_CHECK}} # For Status Inquiry this only means the enquiry worked. Read
160
+ # pp_PaymentResponseCode for the payment outcome: 121 means completed.
161
+ {{SUCCESS_REPORT}}
162
+ else:
163
+ # An unrecognised code is UNRESOLVED, not failed. Resolve it with the
164
+ # Status Inquiry API before refunding or re-charging.
165
+ print('JazzCash call unresolved. Code:', result.get('{{SUCCESS_FIELD}}'))
166
+
167
+ response_hash = result.get('{{RESPONSE_HASH_FIELD}}')
168
+ if isinstance(response_hash, str) and response_hash:
169
+ if not hash_matches(secure_hash(result, integrity_salt), response_hash):
170
+ # JazzCash does not document which response fields feed this hash, so
171
+ # a mismatch means UNVERIFIED, not proven tampering. Reconcile through
172
+ # Status Inquiry rather than rejecting the payment.
173
+ print('JazzCash response hash could not be verified for', result.get('pp_TxnRefNo'))
174
+
175
+ return result
176
+
177
+
178
+ if __name__ == '__main__':
179
+ run()
@@ -0,0 +1,133 @@
1
+ """JazzCash — MWallet linking form
2
+
3
+ Generated by the JazzCash MCP server from {{FLOW_ID}} ({{CORPUS_VERSION}} guides).
4
+ Sources: {{DOC_SOURCES}}
5
+
6
+ - Sandbox and production use the SAME URL. The environment is decided by the
7
+ credentials alone: going live is a credential swap, not a URL change.
8
+
9
+ TWO CORRECTIONS TO THE OFFICIAL SAMPLE, both deliberate:
10
+
11
+ 1. The sample in the guide computes the hash in browser JavaScript with the
12
+ Integrity Salt embedded in the page. The salt is the HMAC key: publishing it
13
+ lets anyone forge a valid pp_SecureHash for your merchant account. This
14
+ module runs server-side and emits only the finished hash.
15
+
16
+ 2. The sample posts to https://pgw.jazzcash.com.pk/..., contradicting the
17
+ endpoint table and screenshot in the same document. This module uses the
18
+ endpoint from the table: {{ENDPOINT}}
19
+
20
+ Requires Python 3.9 or later (zoneinfo).
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import hashlib
26
+ import hmac
27
+ import html
28
+ import os
29
+ import re
30
+ import secrets
31
+ import string
32
+ from datetime import datetime
33
+ from typing import Any, Mapping
34
+ from zoneinfo import ZoneInfo
35
+
36
+ JAZZCASH_LINKING_URL = '{{ENDPOINT}}'
37
+ PAKISTAN = ZoneInfo('Asia/Karachi')
38
+ MSISDN_PATTERN = re.compile(r'^03\d{9}$')
39
+
40
+ # pp_RequestID is annotated [20AN] in the guides: at most 20 letters and digits.
41
+ MAX_REFERENCE_LENGTH = 20
42
+ REFERENCE_ALPHABET = string.ascii_letters + string.digits
43
+
44
+
45
+ def generate_reference(prefix: str, moment: datetime | None = None) -> str:
46
+ """Reference that stays unique inside a single millisecond.
47
+
48
+ The clock alone is not enough: two linking attempts started in the same
49
+ millisecond produce the same pp_RequestID, and the gateway rejects the
50
+ second as a duplicate. Drawn from `secrets`, not `random`.
51
+ """
52
+ stamp = (moment or datetime.now(PAKISTAN)).astimezone(PAKISTAN).strftime('%Y%m%d%H%M%S')
53
+ room = MAX_REFERENCE_LENGTH - len(prefix) - len(stamp)
54
+
55
+ if room < 1:
56
+ raise ValueError(
57
+ f'Reference prefix {prefix!r} leaves no room inside the {MAX_REFERENCE_LENGTH}-character limit.'
58
+ )
59
+
60
+ suffix = ''.join(secrets.choice(REFERENCE_ALPHABET) for _ in range(room))
61
+
62
+ return f'{prefix}{stamp}{suffix}'
63
+
64
+
65
+ def secure_hash(parameters: Mapping[str, Any], integrity_salt: str) -> str:
66
+ values = [
67
+ str(parameters[name])
68
+ for name in sorted(parameters)
69
+ if name != 'pp_SecureHash' and parameters[name] is not None and str(parameters[name]) != ''
70
+ ]
71
+
72
+ message = integrity_salt if not values else integrity_salt + '&' + '&'.join(values)
73
+
74
+ return hmac.new(integrity_salt.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()
75
+
76
+
77
+ def build_linking_parameters(msisdn: str) -> dict[str, str]:
78
+ """Build the signed linking parameters for one customer wallet number."""
79
+ if not MSISDN_PATTERN.match(str(msisdn)):
80
+ raise ValueError('A Pakistani mobile wallet number in the form 03XXXXXXXXX is required.')
81
+
82
+ merchant_id = os.environ.get('JAZZCASH_MERCHANT_ID', '')
83
+ password = os.environ.get('JAZZCASH_PASSWORD', '')
84
+ integrity_salt = os.environ.get('JAZZCASH_INTEGRITY_SALT', '')
85
+ return_url = os.environ.get('JAZZCASH_RETURN_URL', '')
86
+
87
+ if not merchant_id or not password or not integrity_salt or not return_url:
88
+ raise RuntimeError('JazzCash credentials are not configured.')
89
+
90
+ now = datetime.now(PAKISTAN)
91
+
92
+ parameters: dict[str, str] = {
93
+ {{PARAMS}}
94
+ }
95
+ parameters['pp_MerchantID'] = merchant_id
96
+ parameters['pp_Password'] = password
97
+ parameters['pp_MSISDN'] = str(msisdn)
98
+ # Must be unique for every linking attempt.
99
+ parameters['pp_RequestID'] = generate_reference('R', now)
100
+ parameters['pp_ReturnURL'] = return_url
101
+
102
+ parameters['pp_SecureHash'] = secure_hash(parameters, integrity_salt)
103
+
104
+ return parameters
105
+
106
+
107
+ def render_linking_form(parameters: Mapping[str, str]) -> str:
108
+ """Render an auto-submitting linking form. Every value is HTML-escaped."""
109
+ inputs = '\n'.join(
110
+ f' <input type="hidden" name="{html.escape(name, quote=True)}" '
111
+ f'value="{html.escape(str(value), quote=True)}">'
112
+ for name, value in parameters.items()
113
+ )
114
+
115
+ return (
116
+ '<!DOCTYPE html>\n'
117
+ '<html lang="en">\n'
118
+ '<head><meta charset="UTF-8"><title>Link your JazzCash wallet</title></head>\n'
119
+ '<body>\n'
120
+ ' <p>You will be asked for your MPIN on the JazzCash page.</p>\n'
121
+ f' <form id="link-wallet" method="post" action="{html.escape(JAZZCASH_LINKING_URL, quote=True)}">\n'
122
+ f'{inputs}\n'
123
+ ' <noscript><button type="submit">Continue</button></noscript>\n'
124
+ ' </form>\n'
125
+ " <script>document.getElementById('link-wallet').submit();</script>\n"
126
+ '</body>\n'
127
+ '</html>'
128
+ )
129
+
130
+
131
+ # On return, JazzCash posts a payment token to your Return URL. Store it against
132
+ # the customer and charge it later with the token payment API. Call the Token
133
+ # Inquiry API before charging: the customer may have unlinked their wallet.
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@atbas/m-mcp",
3
+ "version": "0.1.0",
4
+ "packageManager": "npm@12.0.2",
5
+ "license": "MIT",
6
+ "description": "MCP server that performs JazzCash payment-gateway integrations inside a merchant's own AI tooling.",
7
+ "author": "Eren Atbas <eren.atbas@gmail.com>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/eatbas/m-mcp.git"
11
+ },
12
+ "homepage": "https://m-mcp.atbas.xyz/",
13
+ "bugs": {
14
+ "url": "https://github.com/eatbas/m-mcp/issues"
15
+ },
16
+ "keywords": [
17
+ "jazzcash",
18
+ "mcp",
19
+ "model-context-protocol",
20
+ "payments",
21
+ "pakistan",
22
+ "payment-gateway",
23
+ "woocommerce",
24
+ "integration"
25
+ ],
26
+ "type": "module",
27
+ "engines": {
28
+ "node": ">=24.18.0"
29
+ },
30
+ "bin": {
31
+ "m-mcp": "dist/index.js"
32
+ },
33
+ "main": "dist/index.js",
34
+ "files": [
35
+ "dist",
36
+ "knowledge",
37
+ "README.md"
38
+ ],
39
+ "scripts": {
40
+ "build": "npm run clean && tsc -p tsconfig.build.json",
41
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
42
+ "prepublishOnly": "npm run release:check",
43
+ "pack:check": "npm run build && npm pack --dry-run",
44
+ "release:check": "npm run scan:credentials && node scripts/require-php.mjs && node scripts/require-python.mjs && npm run typecheck && npm run lint && npm run format:check && npm run test:coverage && npm run pack:check",
45
+ "deploy": "npm run build && node dist/install/cli.js",
46
+ "deploy:list": "npm run build && node dist/install/cli.js --list",
47
+ "dev": "tsx src/index.ts",
48
+ "lint": "eslint .",
49
+ "lint:fix": "eslint . --fix",
50
+ "format": "prettier --write .",
51
+ "format:check": "prettier --check .",
52
+ "typecheck": "tsc --noEmit",
53
+ "test": "vitest run",
54
+ "test:fast": "vitest run tests/domain tests/validation tests/catalogue tests/config tests/telemetry",
55
+ "test:changed": "vitest related --run",
56
+ "test:coverage": "vitest run --coverage",
57
+ "extract-pdf-text": "node scripts/extract-pdf-text.mjs",
58
+ "scan:credentials": "node scripts/scan-credentials.mjs"
59
+ },
60
+ "dependencies": {
61
+ "@modelcontextprotocol/sdk": "^1.29.0",
62
+ "zod": "^4.4.3"
63
+ },
64
+ "devDependencies": {
65
+ "@eslint/js": "^10.0.1",
66
+ "@types/node": "^24.13.3",
67
+ "@vitest/coverage-v8": "^4.1.10",
68
+ "eslint": "^10.8.0",
69
+ "eslint-config-prettier": "^10.0.1",
70
+ "globals": "^17.8.0",
71
+ "prettier": "^3.4.2",
72
+ "tsx": "^4.19.2",
73
+ "typescript": "^6.0.3",
74
+ "typescript-eslint": "^8.23.0",
75
+ "vitest": "^4.1.10"
76
+ },
77
+ "publishConfig": {
78
+ "access": "public"
79
+ },
80
+ "allowScripts": {
81
+ "esbuild@0.28.2": true,
82
+ "fsevents": false
83
+ }
84
+ }