mirror of
https://github.com/MAGICGrants/campaign-site.git
synced 2026-01-09 12:27:59 -05:00
feat: donation confirmation email
This commit is contained in:
@@ -3,3 +3,5 @@ export const MIN_AMOUNT = 1
|
||||
export const MAX_AMOUNT = 5000
|
||||
export const AMOUNT_STEP = 5
|
||||
export const MEMBERSHIP_PRICE = 100
|
||||
export const POINTS_PER_USD = 1
|
||||
export const POINTS_REDEEM_PRICE_USD = 0.1
|
||||
|
||||
328
package-lock.json
generated
328
package-lock.json
generated
@@ -60,8 +60,12 @@
|
||||
"react-hook-form": "^7.51.5",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-social-icons": "^6.18.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-html": "^16.0.1",
|
||||
"remark-rehype": "^11.1.1",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"sharp": "^0.33.4",
|
||||
"stripe": "^15.9.0",
|
||||
@@ -8461,6 +8465,44 @@
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-table": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
|
||||
"integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz",
|
||||
"integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"unist-util-is": "^6.0.0",
|
||||
"unist-util-visit-parents": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-from-markdown": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz",
|
||||
@@ -8485,6 +8527,107 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz",
|
||||
"integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-gfm-autolink-literal": "^2.0.0",
|
||||
"mdast-util-gfm-footnote": "^2.0.0",
|
||||
"mdast-util-gfm-strikethrough": "^2.0.0",
|
||||
"mdast-util-gfm-table": "^2.0.0",
|
||||
"mdast-util-gfm-task-list-item": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-autolink-literal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
|
||||
"integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"ccount": "^2.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"mdast-util-find-and-replace": "^3.0.0",
|
||||
"micromark-util-character": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-footnote": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz",
|
||||
"integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"devlop": "^1.1.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0",
|
||||
"micromark-util-normalize-identifier": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-strikethrough": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
|
||||
"integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-table": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
|
||||
"integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"markdown-table": "^3.0.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-task-list-item": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
|
||||
"integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-phrasing": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
|
||||
@@ -8737,6 +8880,127 @@
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
|
||||
"integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-extension-gfm-autolink-literal": "^2.0.0",
|
||||
"micromark-extension-gfm-footnote": "^2.0.0",
|
||||
"micromark-extension-gfm-strikethrough": "^2.0.0",
|
||||
"micromark-extension-gfm-table": "^2.0.0",
|
||||
"micromark-extension-gfm-tagfilter": "^2.0.0",
|
||||
"micromark-extension-gfm-task-list-item": "^2.0.0",
|
||||
"micromark-util-combine-extensions": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-autolink-literal": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
|
||||
"integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-sanitize-uri": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-footnote": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
|
||||
"integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-core-commonmark": "^2.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-normalize-identifier": "^2.0.0",
|
||||
"micromark-util-sanitize-uri": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-strikethrough": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
|
||||
"integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-util-chunked": "^2.0.0",
|
||||
"micromark-util-classify-character": "^2.0.0",
|
||||
"micromark-util-resolve-all": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-table": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz",
|
||||
"integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-tagfilter": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
|
||||
"integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-task-list-item": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
|
||||
"integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-factory-destination": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz",
|
||||
@@ -10500,6 +10764,35 @@
|
||||
"jsesc": "bin/jsesc"
|
||||
}
|
||||
},
|
||||
"node_modules/rehype-sanitize": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz",
|
||||
"integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"hast-util-sanitize": "^5.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/rehype-stringify": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz",
|
||||
"integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"hast-util-to-html": "^9.0.0",
|
||||
"unified": "^11.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz",
|
||||
@@ -10516,6 +10809,24 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-gfm": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz",
|
||||
"integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-gfm": "^3.0.0",
|
||||
"micromark-extension-gfm": "^3.0.0",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-stringify": "^11.0.0",
|
||||
"unified": "^11.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-html": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-html/-/remark-html-16.0.1.tgz",
|
||||
@@ -10549,6 +10860,23 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-rehype": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz",
|
||||
"integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-to-hast": "^13.0.0",
|
||||
"unified": "^11.0.0",
|
||||
"vfile": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-stringify": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
|
||||
|
||||
@@ -62,8 +62,12 @@
|
||||
"react-hook-form": "^7.51.5",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-social-icons": "^6.18.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-html": "^16.0.1",
|
||||
"remark-rehype": "^11.1.1",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"sharp": "^0.33.4",
|
||||
"stripe": "^15.9.0",
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '../../../server/types'
|
||||
import { btcpayApi as _btcpayApi, btcpayApi, prisma } from '../../../server/services'
|
||||
import { env } from '../../../env.mjs'
|
||||
import axios from 'axios'
|
||||
import { sendDonationConfirmationEmail } from '../../../server/utils/mailing'
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
@@ -119,6 +119,20 @@ async function handleBtcpayWebhook(req: NextApiRequest, res: NextApiResponse) {
|
||||
body.metadata.isMembership === 'true' ? dayjs().add(1, 'year').toDate() : null,
|
||||
},
|
||||
})
|
||||
|
||||
if (body.metadata.donorEmail && body.metadata.donorName) {
|
||||
sendDonationConfirmationEmail({
|
||||
to: body.metadata.donorEmail,
|
||||
donorName: body.metadata.donorName,
|
||||
fundSlug: body.metadata.fundSlug,
|
||||
projectName: body.metadata.projectName,
|
||||
isMembership: body.metadata.isMembership === 'true',
|
||||
isSubscription: false,
|
||||
pointsReceived: 0,
|
||||
btcpayAsset: paymentMethod.cryptoCode as 'BTC' | 'XMR',
|
||||
btcpayCryptoAmount: cryptoAmount,
|
||||
})
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
115
server/utils/mailing.ts
Normal file
115
server/utils/mailing.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { FundSlug } from '@prisma/client'
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { env } from '../../env.mjs'
|
||||
import { transporter } from '../services'
|
||||
import { funds } from '../../utils/funds'
|
||||
import { POINTS_REDEEM_PRICE_USD } from '../../config'
|
||||
import markdownToHtml from '../../utils/markdownToHtml'
|
||||
|
||||
dayjs.extend(localizedFormat)
|
||||
|
||||
const pointsFormat = Intl.NumberFormat('en', { notation: 'standard', compactDisplay: 'long' })
|
||||
|
||||
type SendDonationConfirmationEmailParams = {
|
||||
to: string
|
||||
donorName: string
|
||||
fundSlug: FundSlug
|
||||
projectName?: string
|
||||
isMembership: boolean
|
||||
isSubscription: boolean
|
||||
stripeUsdAmount?: number
|
||||
btcpayCryptoAmount?: number
|
||||
btcpayAsset?: 'BTC' | 'XMR'
|
||||
pointsReceived: number
|
||||
}
|
||||
|
||||
export async function sendDonationConfirmationEmail({
|
||||
to,
|
||||
donorName,
|
||||
fundSlug,
|
||||
projectName,
|
||||
isMembership,
|
||||
isSubscription,
|
||||
stripeUsdAmount,
|
||||
btcpayCryptoAmount,
|
||||
btcpayAsset,
|
||||
pointsReceived,
|
||||
}: SendDonationConfirmationEmailParams) {
|
||||
const dateStr = dayjs().format('YYYY-M-D')
|
||||
const fundName = funds[fundSlug].title
|
||||
|
||||
const markdown = `Thank you for your donation to MAGIC Grants! Your donation supports our charitable mission.
|
||||
|
||||
${
|
||||
!isMembership
|
||||
? `You donated to: ${fundName}
|
||||
${projectName ? `You supported this campaign: ${projectName}` : ''}`
|
||||
: ''
|
||||
}
|
||||
|
||||
${
|
||||
isMembership
|
||||
? `You purchased an annual membership for the ${fundName}.
|
||||
This membership ${isSubscription ? 'will' : 'will not'} renew automatically. Easily manage your membership by logging into your account at donate.magicgrants.org.`
|
||||
: ''
|
||||
}
|
||||
|
||||
Please see the full details on your donation receipt below:
|
||||
|
||||
Multidisciplinary Academic Grants in Cryptocurrencies (MAGIC Grants) is a 501(c)(3) exempt public charity. This serves as your donation receipt. Donations to MAGIC Grants are tax deductible to the extent allowable by law.
|
||||
|
||||
Donation Date: ${dateStr}
|
||||
|
||||
Donor Information:
|
||||
${donorName}
|
||||
|
||||
MAGIC Grants acknowledges and expresses appreciation for the following contribution:
|
||||
- [${stripeUsdAmount ? 'x' : ' '}] Cash or bank transfer donation amount: ${stripeUsdAmount ? stripeUsdAmount.toFixed(2) : 'N/A'}
|
||||
- [${btcpayCryptoAmount ? 'x' : ' '}] In-kind (non-fiat) donation description: ${btcpayCryptoAmount} ${btcpayAsset}
|
||||
|
||||
Description and/or restrictions: ${fundSlug === 'general' ? 'None' : `Donation to the ${fundName}`}
|
||||
|
||||
The following describes the context of your donation:
|
||||
|
||||
- [${!pointsReceived ? 'x' : ' '}] No goods or services were received in exchange for your generous donation.
|
||||
- [${pointsReceived ? 'x' : ' '}] In connection with your generous donation, you received ${pointsFormat.format(pointsReceived)} points, valued at approximately $${(pointsReceived * POINTS_REDEEM_PRICE_USD).toFixed(2)}.
|
||||
|
||||
${btcpayCryptoAmount ? 'If you wish to receive a tax deduction for a cryptocurrency donation over $500, you MUST complete [Form 8283](https://www.irs.gov/pub/irs-pdf/f8283.pdf) and send the completed form to [info@magicgrants.org](mailto:info@magicgrants.org) to deduct your donation.' : ''}
|
||||
|
||||
Multidisciplinary Academic Grants in Cryptocurrencies
|
||||
1942 Broadway St., STE 314C
|
||||
Boulder, CO 80302
|
||||
EIN: 82-5183590
|
||||
(303) 900-3237
|
||||
info@magicgrants.org`
|
||||
|
||||
const htmlFromMarkdown = await markdownToHtml(markdown)
|
||||
|
||||
const html = `<style>
|
||||
html {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 700px;
|
||||
padding: 20px;
|
||||
margin: 0 auto;
|
||||
font-family: sans-serif;
|
||||
background-color: #F1F5FF;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3a76f0;
|
||||
}
|
||||
</style>
|
||||
|
||||
${htmlFromMarkdown}`
|
||||
|
||||
return transporter.sendMail({
|
||||
from: env.SES_VERIFIED_SENDER,
|
||||
to,
|
||||
html,
|
||||
})
|
||||
}
|
||||
@@ -2,10 +2,10 @@ import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import Stripe from 'stripe'
|
||||
import getRawBody from 'raw-body'
|
||||
import dayjs from 'dayjs'
|
||||
import crypto from 'crypto'
|
||||
|
||||
import { btcpayApi as _btcpayApi, prisma, stripe } from '../../server/services'
|
||||
import { DonationMetadata } from '../../server/types'
|
||||
import { sendDonationConfirmationEmail } from './mailing'
|
||||
|
||||
export function getStripeWebhookHandler(secret: string) {
|
||||
return async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
@@ -45,6 +45,19 @@ export function getStripeWebhookHandler(secret: string) {
|
||||
metadata.isMembership === 'true' ? dayjs().add(1, 'year').toDate() : null,
|
||||
},
|
||||
})
|
||||
|
||||
if (metadata.donorEmail && metadata.donorName) {
|
||||
sendDonationConfirmationEmail({
|
||||
to: metadata.donorEmail,
|
||||
donorName: metadata.donorName,
|
||||
fundSlug: metadata.fundSlug,
|
||||
projectName: metadata.projectName,
|
||||
isMembership: metadata.isMembership === 'true',
|
||||
isSubscription: metadata.isSubscription === 'true',
|
||||
stripeUsdAmount: paymentIntent.amount_received / 100,
|
||||
pointsReceived: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Store subscription data when subscription invoice is paid
|
||||
@@ -69,6 +82,19 @@ export function getStripeWebhookHandler(secret: string) {
|
||||
membershipExpiresAt: new Date(invoiceLine.period.end * 1000),
|
||||
},
|
||||
})
|
||||
|
||||
if (metadata.donorEmail && metadata.donorName) {
|
||||
sendDonationConfirmationEmail({
|
||||
to: metadata.donorEmail,
|
||||
donorName: metadata.donorName,
|
||||
fundSlug: metadata.fundSlug,
|
||||
projectName: metadata.projectName,
|
||||
isMembership: metadata.isMembership === 'true',
|
||||
isSubscription: metadata.isSubscription === 'true',
|
||||
stripeUsdAmount: invoice.total / 100,
|
||||
pointsReceived: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import { remark } from 'remark'
|
||||
import html from 'remark-html'
|
||||
import { unified } from 'unified'
|
||||
import rehypeStringify from 'rehype-stringify'
|
||||
import remarkSanitize from 'rehype-sanitize'
|
||||
import remarkRehype from 'remark-rehype'
|
||||
import remarkParse from 'remark-parse'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
|
||||
export default async function markdownToHtml(markdown: string) {
|
||||
const result = await remark().use(html).process(markdown)
|
||||
const result = await unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkGfm)
|
||||
.use(remarkRehype)
|
||||
.use(rehypeStringify)
|
||||
.use(remarkSanitize)
|
||||
.process(markdown)
|
||||
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user