diff --git a/package-lock.json b/package-lock.json index 5a63c5409..9bfcde4b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3113,6 +3113,8 @@ }, "node_modules/@expo/cli/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/@expo/cli/node_modules/getenv": { @@ -3301,6 +3303,8 @@ }, "node_modules/@expo/config-plugins/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/@expo/config-plugins/node_modules/getenv": { @@ -3370,6 +3374,8 @@ }, "node_modules/@expo/config-types": { "version": "53.0.5", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-53.0.5.tgz", + "integrity": "sha512-kqZ0w44E+HEGBjy+Lpyn0BVL5UANg/tmNixxaRMLS6nf37YsDrLk2VMAmeKMMk5CKG0NmOdVv3ngeUjRQMsy9g==", "license": "MIT" }, "node_modules/@expo/config/node_modules/@babel/code-frame": { @@ -3524,6 +3530,8 @@ }, "node_modules/@expo/env/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/@expo/env/node_modules/dotenv": { @@ -3620,6 +3628,8 @@ }, "node_modules/@expo/fingerprint/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/@expo/fingerprint/node_modules/find-up": { @@ -3794,6 +3804,8 @@ }, "node_modules/@expo/image-utils/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/@expo/image-utils/node_modules/getenv": { @@ -3909,6 +3921,8 @@ }, "node_modules/@expo/metro-config/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/@expo/metro-config/node_modules/dotenv": { @@ -4064,6 +4078,8 @@ }, "node_modules/@expo/package-manager/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/@expo/package-manager/node_modules/has-flag": { @@ -4134,6 +4150,8 @@ }, "node_modules/@expo/schema-utils": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-0.1.0.tgz", + "integrity": "sha512-Me2avOfbcVT/O5iRmPKLCCSvbCfVfxIstGMlzVJOffplaZX1+ut8D18siR1wx5fkLMTWKs14ozEz11cGUY7hcw==", "license": "MIT" }, "node_modules/@expo/sdk-runtime-versions": { @@ -4152,6 +4170,8 @@ }, "node_modules/@expo/sudo-prompt": { "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz", + "integrity": "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==", "license": "MIT" }, "node_modules/@expo/vector-icons": { @@ -4163,6 +4183,8 @@ }, "node_modules/@expo/ws-tunnel": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.6.tgz", + "integrity": "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==", "license": "MIT" }, "node_modules/@expo/xcpretty": { @@ -4200,6 +4222,8 @@ }, "node_modules/@expo/xcpretty/node_modules/argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, "node_modules/@expo/xcpretty/node_modules/chalk": { @@ -4228,6 +4252,8 @@ }, "node_modules/@expo/xcpretty/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/@expo/xcpretty/node_modules/find-up": { @@ -9560,6 +9586,8 @@ }, "node_modules/@react-native/dev-middleware/node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/@react-native/dev-middleware/node_modules/ws": { @@ -9579,6 +9607,8 @@ }, "node_modules/@react-native/normalize-colors": { "version": "0.79.5", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.79.5.tgz", + "integrity": "sha512-nGXMNMclZgzLUxijQQ38Dm3IAEhgxuySAWQHnljFtfB0JdaMwpe0Ox9H7Tp2OgrEA+EMEv+Od9ElKlHwGKmmvQ==", "license": "MIT" }, "node_modules/@react-native/virtualized-lists": { @@ -12519,6 +12549,8 @@ }, "node_modules/babel-plugin-react-native-web": { "version": "0.19.13", + "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.13.tgz", + "integrity": "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ==", "license": "MIT" }, "node_modules/babel-plugin-syntax-hermes-parser": { @@ -15779,6 +15811,8 @@ }, "node_modules/exec-async": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", + "integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==", "license": "MIT" }, "node_modules/execa": { @@ -16207,6 +16241,8 @@ }, "node_modules/expo-modules-autolinking/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/expo-modules-autolinking/node_modules/find-up": { @@ -16349,6 +16385,8 @@ }, "node_modules/fast-base64-decode": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz", + "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==", "license": "MIT" }, "node_modules/fast-deep-equal": { @@ -16402,6 +16440,8 @@ }, "node_modules/fast-text-encoding": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", "license": "Apache-2.0" }, "node_modules/fast-xml-parser": { @@ -16711,6 +16751,8 @@ }, "node_modules/fontfaceobserver": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", + "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", "license": "BSD-2-Clause" }, "node_modules/for-each": { @@ -17330,6 +17372,8 @@ }, "node_modules/hermes-estree": { "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", "license": "MIT" }, "node_modules/hermes-parser": { @@ -20090,6 +20134,8 @@ }, "node_modules/jimp-compact": { "version": "0.16.1", + "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", + "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", "license": "MIT" }, "node_modules/jiti": { @@ -21171,6 +21217,8 @@ }, "node_modules/lighthouse-logger/node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/lightningcss": { @@ -21200,6 +21248,126 @@ "lightningcss-win32-x64-msvc": "1.27.0" } }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz", + "integrity": "sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.27.0.tgz", + "integrity": "sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz", + "integrity": "sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz", + "integrity": "sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz", + "integrity": "sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz", + "integrity": "sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.27.0", "cpu": [ @@ -21236,6 +21404,46 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz", + "integrity": "sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.27.0.tgz", + "integrity": "sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "license": "MIT", @@ -21600,6 +21808,8 @@ }, "node_modules/marky": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", "license": "Apache-2.0" }, "node_modules/math-intrinsics": { @@ -22918,6 +23128,8 @@ }, "node_modules/nested-error-stacks": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", + "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", "license": "MIT" }, "node_modules/nocache": { @@ -26049,6 +26261,8 @@ }, "node_modules/qrcode-terminal": { "version": "0.11.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", + "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" } @@ -27035,6 +27249,8 @@ }, "node_modules/requireg": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", + "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", "dependencies": { "nested-error-stacks": "~2.0.1", "rc": "~1.2.7", @@ -27605,6 +27821,8 @@ }, "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/send/node_modules/encodeurl": { @@ -28328,6 +28546,8 @@ }, "node_modules/structured-headers": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", + "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", "license": "MIT" }, "node_modules/style-inject": { @@ -30572,6 +30792,8 @@ }, "node_modules/wonka": { "version": "6.3.5", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", + "integrity": "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==", "license": "MIT" }, "node_modules/word-wrap": { @@ -32616,6 +32838,7 @@ "dependencies": { "@babel/runtime": "^7.25.7", "@onflow/fcl-core": "^1.30.1", + "@onflow/payments": "0.0.1", "@onflow/typedefs": "^1.8.0", "@tanstack/react-query": "^5.67.3" }, @@ -32658,6 +32881,7 @@ "dependencies": { "@babel/runtime": "^7.25.7", "@headlessui/react": "^2.2.2", + "@onflow/payments": "0.0.1", "@onflow/react-core": "0.8.1", "@tanstack/react-query": "^5.67.3", "@testing-library/react": "^16.2.0", diff --git a/packages/payments/src/client.ts b/packages/payments/src/client.ts index 1a2494b52..ca0704a9f 100644 --- a/packages/payments/src/client.ts +++ b/packages/payments/src/client.ts @@ -3,6 +3,7 @@ import type { FundingSession, FundingProvider, FundingProviderFactory, + ProviderCapability, } from "./types" import type {createFlowClientCore} from "@onflow/fcl-core" import {ADDRESS_PATTERN} from "./constants" @@ -18,6 +19,11 @@ export interface PaymentsClient { * @returns Promise resolving to a funding session with instructions */ createSession(intent: FundingIntent): Promise + /** + * Get capabilities from all configured providers + * @returns Promise resolving to an array of provider capabilities + */ + getCapabilities(): Promise } /** @@ -134,5 +140,13 @@ export function createPaymentsClient( `Failed to create session: no provider could handle the request. Errors: ${errorDetails}` ) }, + async getCapabilities() { + const allCapabilities: ProviderCapability[] = [] + for (const provider of providers) { + const capabilities = await provider.getCapabilities() + allCapabilities.push(...capabilities) + } + return allCapabilities + }, } } diff --git a/packages/payments/tsconfig.json b/packages/payments/tsconfig.json index 520b8dcd8..7b3089958 100644 --- a/packages/payments/tsconfig.json +++ b/packages/payments/tsconfig.json @@ -1,9 +1,9 @@ { "extends": "../../tsconfig", - "include": ["src/**/*", "src/**/*.d.ts", "cadence/**/*", "flow.json"], + "include": ["src/**/*", "src/**/*.d.ts"], "compilerOptions": { "declarationDir": "types", - "rootDir": ".", + "rootDir": "src", "resolveJsonModule": true } } diff --git a/packages/react-core/package.json b/packages/react-core/package.json index 2be28d010..fb54a9172 100644 --- a/packages/react-core/package.json +++ b/packages/react-core/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@onflow/fcl-core": "^1.30.1", + "@onflow/payments": "0.0.1", "@onflow/typedefs": "^1.8.0", "@babel/runtime": "^7.25.7", "@tanstack/react-query": "^5.67.3" diff --git a/packages/react-core/src/core/context.ts b/packages/react-core/src/core/context.ts index f8369bb21..2e138b679 100644 --- a/packages/react-core/src/core/context.ts +++ b/packages/react-core/src/core/context.ts @@ -1,6 +1,7 @@ import {createContext} from "react" import type {FlowClientCore} from "@onflow/fcl-core" import type {FlowNetwork} from "./types" +import type {PaymentsClient} from "@onflow/payments" // FlowConfig based on @onflow/fcl-core's FlowClientCoreConfig // This matches the config structure used by both @onflow/fcl and @onflow/fcl-react-native @@ -38,3 +39,7 @@ export type FlowConfig = { export const FlowConfigContext = createContext({}) export const FlowClientContext = createContext(null) + +export const PaymentsClientContext = createContext( + undefined +) diff --git a/packages/react-core/src/hooks/index.ts b/packages/react-core/src/hooks/index.ts index c25dd4d9c..4a0a49771 100644 --- a/packages/react-core/src/hooks/index.ts +++ b/packages/react-core/src/hooks/index.ts @@ -26,3 +26,6 @@ export * from "./useFlowScheduledTransactionList" export * from "./useFlowScheduledTransaction" export * from "./useFlowScheduledTransactionSetup" export * from "./useFlowScheduledTransactionCancel" +export * from "./useFund" +export * from "./useFundingCapabilities" +export * from "./usePaymentsClient" diff --git a/packages/react-core/src/hooks/useFund.test.ts b/packages/react-core/src/hooks/useFund.test.ts new file mode 100644 index 000000000..98694409e --- /dev/null +++ b/packages/react-core/src/hooks/useFund.test.ts @@ -0,0 +1,95 @@ +import {renderHook, waitFor} from "@testing-library/react" +import {useFund} from "./useFund" +import {FundingIntent, FundingSession, PaymentsClient} from "@onflow/payments" +import {TestProvider} from "../__mocks__/TestProvider" + +describe("useFund", () => { + const mockSession: FundingSession = { + provider: "test-provider", + instructions: { + kind: "crypto", + address: "0xTestDepositAddress", + chain: "eip155:1", + currency: "0xUSDC", + }, + } + + const mockIntent: FundingIntent = { + kind: "crypto", + destination: "eip155:747:0xRecipient", + currency: "0xUSDC", + amount: "100", + sourceChain: "eip155:1", + sourceCurrency: "0xUSDC", + } + + // Create mock payments client + const createMockPaymentsClient = (shouldSucceed: boolean): PaymentsClient => { + return { + createSession: jest + .fn() + .mockResolvedValue( + shouldSucceed + ? mockSession + : Promise.reject(new Error("Provider failed")) + ), + } as any + } + + beforeEach(() => { + jest.clearAllMocks() + }) + + it("should successfully create a funding session", async () => { + const mockClient = createMockPaymentsClient(true) + + const {result} = renderHook(() => useFund({paymentsClient: mockClient}), { + wrapper: TestProvider, + }) + + // Trigger the mutation + result.current.mutateAsync(mockIntent) + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true) + }) + + expect(result.current.data).toEqual(mockSession) + expect(mockClient.createSession).toHaveBeenCalledWith(mockIntent) + }) + + it("should handle funding session errors", async () => { + const error = new Error("Provider failed") + const mockClient = createMockPaymentsClient(false) + + const {result} = renderHook(() => useFund({paymentsClient: mockClient}), { + wrapper: TestProvider, + }) + + // Trigger the mutation + result.current.mutate(mockIntent) + + await waitFor(() => { + expect(result.current.isError).toBe(true) + }) + + expect(result.current.error?.message).toContain("Provider failed") + }) + + it("should throw error when no payments client is provided", async () => { + const {result} = renderHook(() => useFund(), { + wrapper: TestProvider, + }) + + // Trigger the mutation + result.current.mutate(mockIntent) + + await waitFor(() => { + expect(result.current.isError).toBe(true) + }) + + expect(result.current.error?.message).toContain( + "No payments client available" + ) + }) +}) diff --git a/packages/react-core/src/hooks/useFund.ts b/packages/react-core/src/hooks/useFund.ts new file mode 100644 index 000000000..e722e02af --- /dev/null +++ b/packages/react-core/src/hooks/useFund.ts @@ -0,0 +1,89 @@ +import { + useMutation, + UseMutationResult, + UseMutationOptions, +} from "@tanstack/react-query" +import {useCallback} from "react" +import {useFlowQueryClient} from "../provider/FlowQueryClient" +import {useFlowClient} from "./useFlowClient" +import {usePaymentsClient} from "./usePaymentsClient" +import {FundingIntent, FundingSession, PaymentsClient} from "@onflow/payments" + +/** + * Arguments for the useFund hook. + */ +export interface UseFundArgs { + /** Optional payments client (uses context if not provided) */ + paymentsClient?: PaymentsClient + /** Optional React Query mutation settings (e.g., `onSuccess`, `onError`, `retry`) */ + mutation?: Omit< + UseMutationOptions, + "mutationFn" + > + /** Optional Flow client override */ + flowClient?: ReturnType +} + +/** + * useFund + * + * Creates a funding session via the payments client and returns a React Query mutation. + * Use this hook to initiate crypto or fiat funding flows. + * + * @param args.paymentsClient - Optional payments client (uses context if not provided) + * @param args.mutation - Optional React Query mutation options + * @param args.flowClient - Optional Flow client override + * + * @example + * ```tsx + * import { useFund } from "@onflow/react-sdk" + * + * function FundButton() { + * const { mutateAsync: fund, isPending } = useFund() + * + * const handleFund = async () => { + * const session = await fund({ + * kind: "crypto", + * destination: "eip155:747:0xRecipient", + * currency: "0xUSDC", + * amount: "100", + * sourceChain: "eip155:1", + * sourceCurrency: "0xUSDC", + * }) + * console.log("Deposit to:", session.instructions.address) + * } + * + * return + * } + * ``` + */ +export function useFund({ + paymentsClient: _paymentsClient, + mutation: mutationOptions = {}, + flowClient, +}: UseFundArgs = {}): UseMutationResult { + const queryClient = useFlowQueryClient() + const contextPaymentsClient = usePaymentsClient() + const paymentsClient = _paymentsClient || contextPaymentsClient + + const mutationFn = useCallback( + async (intent: FundingIntent) => { + if (!paymentsClient) { + throw new Error( + "No payments client available. Configure fundingProviders in FlowProvider or pass paymentsClient to useFund." + ) + } + return paymentsClient.createSession(intent) + }, + [paymentsClient] + ) + + return useMutation( + { + mutationFn, + retry: false, + ...mutationOptions, + }, + queryClient + ) +} diff --git a/packages/react-core/src/hooks/useFundingCapabilities.ts b/packages/react-core/src/hooks/useFundingCapabilities.ts new file mode 100644 index 000000000..5cd7d5c30 --- /dev/null +++ b/packages/react-core/src/hooks/useFundingCapabilities.ts @@ -0,0 +1,99 @@ +import {useQuery, UseQueryOptions, UseQueryResult} from "@tanstack/react-query" +import {useCallback} from "react" +import {useFlowQueryClient} from "../provider/FlowQueryClient" +import {useFlowClient} from "./useFlowClient" +import {usePaymentsClient} from "./usePaymentsClient" +import {useFlowChainId} from "./useFlowChainId" +import {ProviderCapability, PaymentsClient} from "@onflow/payments" + +/** + * Arguments for the useFundingCapabilities hook. + */ +export interface UseFundingCapabilitiesArgs { + /** Optional payments client (uses context if not provided) */ + paymentsClient?: PaymentsClient + /** Optional React Query options */ + query?: Omit< + UseQueryOptions, + "queryKey" | "queryFn" + > + /** Optional Flow client override */ + flowClient?: ReturnType +} + +/** + * useFundingCapabilities + * + * Fetches the capabilities (supported chains, currencies, etc.) from funding providers. + * Use this to dynamically populate UI with available funding options. + * + * @param args.paymentsClient - Optional payments client (uses context if not provided) + * @param args.query - Optional React Query options + * @param args.flowClient - Optional Flow client override + * + * @example + * ```tsx + * import { useFundingCapabilities } from "@onflow/react-sdk" + * + * function FundingOptions() { + * const { data: capabilities, isLoading } = useFundingCapabilities() + * + * if (isLoading) return
Loading...
+ * + * const cryptoCapability = capabilities?.find(c => c.type === "crypto") + * const chains = cryptoCapability?.sourceChains || [] + * const currencies = cryptoCapability?.currencies || [] + * + * return ( + *
+ *

Supported Chains:

+ *
    {chains.map(chain =>
  • {chain}
  • )}
+ *

Supported Currencies:

+ *
    {currencies.map(currency =>
  • {currency}
  • )}
+ *
+ * ) + * } + * ``` + */ +export function useFundingCapabilities({ + paymentsClient: _paymentsClient, + query: queryOptions = {}, + flowClient, +}: UseFundingCapabilitiesArgs = {}): UseQueryResult< + ProviderCapability[], + Error +> { + const queryClient = useFlowQueryClient() + const contextPaymentsClient = usePaymentsClient() + const paymentsClient = _paymentsClient || contextPaymentsClient + const {data: chainId} = useFlowChainId() + + // Use chainId in query key for proper cache invalidation when network switches + const chainIdForKey = chainId || "unknown" + + const fetchCapabilities = useCallback(async () => { + if (!paymentsClient) { + throw new Error( + "No payments client available. Configure fundingProviders in FlowProvider or pass paymentsClient to useFundingCapabilities." + ) + } + + // Use the getCapabilities method from the payments client + return await paymentsClient.getCapabilities() + }, [paymentsClient]) + + return useQuery( + { + queryKey: [ + "fundingCapabilities", + paymentsClient ? "configured" : "none", + chainIdForKey, + ], + queryFn: fetchCapabilities, + enabled: !!paymentsClient, + staleTime: 5 * 60 * 1000, // 5 minutes - capabilities don't change often + ...queryOptions, + }, + queryClient + ) +} diff --git a/packages/react-core/src/hooks/usePaymentsClient.ts b/packages/react-core/src/hooks/usePaymentsClient.ts new file mode 100644 index 000000000..dc5f9575a --- /dev/null +++ b/packages/react-core/src/hooks/usePaymentsClient.ts @@ -0,0 +1,10 @@ +import {useContext} from "react" +import {PaymentsClientContext} from "../core/context" + +/** + * Hook to access the PaymentsClient from FlowProvider context + * @returns The PaymentsClient instance or undefined if not configured + */ +export function usePaymentsClient() { + return useContext(PaymentsClientContext) +} diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index ac75053d3..f7f692794 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -49,6 +49,7 @@ "dependencies": { "@babel/runtime": "^7.25.7", "@headlessui/react": "^2.2.2", + "@onflow/payments": "0.0.1", "@onflow/react-core": "0.8.1", "@tanstack/react-query": "^5.67.3", "@testing-library/react": "^16.2.0", diff --git a/packages/react-sdk/src/core/types.ts b/packages/react-sdk/src/core/types.ts new file mode 100644 index 000000000..dc8bff394 --- /dev/null +++ b/packages/react-sdk/src/core/types.ts @@ -0,0 +1 @@ +export type FlowNetwork = "emulator" | "testnet" | "mainnet"