CINXE.COM

Run Payments with Stripe | Firebase Extensions Hub

<!-- * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. --><!DOCTYPE html><html lang="en" dir="ltr"><head> <title>Run Payments with Stripe | Firebase Extensions Hub</title> <base href="/"> <meta charset="utf-8"> <meta http-equiv="Content-Type"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta property="og:type" content="website"> <link rel="icon" sizes="192x192" type="image/png" href="/assets/favicons/favicon.png"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin=""> <style type="text/css">@font-face{font-family:'Roboto';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCRc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;}@font-face{font-family:'Roboto';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fABc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;}@font-face{font-family:'Roboto';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4AMP6lbBP.woff2) format('woff2');unicode-range:U+1F00-1FFF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBxc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;}@font-face{font-family:'Roboto';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4AMP6lQ.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}@font-face{font-family:'Roboto';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;}@font-face{font-family:'Roboto';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKKTU1Kvnz.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;}@font-face{font-family:'Roboto';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKKTU1Kvnz.woff2) format('woff2');unicode-range:U+1F00-1FFF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKKTU1Kvnz.woff2) format('woff2');unicode-range:U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKKTU1Kvnz.woff2) format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;}@font-face{font-family:'Roboto';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2) format('woff2');unicode-range:U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmEU9fCRc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmEU9fABc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmEU9fCBc4AMP6lbBP.woff2) format('woff2');unicode-range:U+1F00-1FFF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmEU9fBxc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmEU9fCxc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmEU9fChc4AMP6lbBP.woff2) format('woff2');unicode-range:U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}</style> <style type="text/css">@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiIUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0308, U+0530-058F, U+2010, U+2024, U+25CC, U+FB13-FB17;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiAUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0980-09FE, U+1CD0, U+1CD2, U+1CD5-1CD6, U+1CD8, U+1CE1, U+1CEA, U+1CED, U+1CF2, U+1CF5-1CF7, U+200C-200D, U+20B9, U+25CC, U+A8F1;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPj8UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjYUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjMUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiMUvbQoi-Entw.woff2) format('woff2');unicode-range:U+1200-1399, U+2D80-2DDE, U+AB01-AB2E, U+1E7E0-1E7E6, U+1E7E8-1E7EB, U+1E7ED-1E7EE, U+1E7F0-1E7FE;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPi0UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0589, U+10A0-10FF, U+1C90-1CBA, U+1CBD-1CBF, U+2D00-2D2F;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjEUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPikUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0A80-0AFF, U+200C-200D, U+20B9, U+25CC, U+A830-A839;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPhEUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0A01-0A76, U+200C-200D, U+20B9, U+25CC, U+262C, U+A830-A839;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjAUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjkUvbQoi-Entw.woff2) format('woff2');unicode-range:U+1780-17FF, U+19E0-19FF, U+200C-200D, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjsUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0E81-0EDF, U+200C-200D, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPisUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0B01-0B77, U+1CDA, U+1CF2, U+200C-200D, U+20B9, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPi8UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0964-0965, U+0D81-0DF4, U+1CF2, U+200C-200D, U+25CC, U+111E1-111F4;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPlwUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8B1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA88, U+1FA90-1FABD, U+1FABF-1FAC5, U+1FACE-1FADB, U+1FAE0-1FAE8, U+1FAF0-1FAF8, U+1FB00-1FBFF;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiQUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0964-0965, U+0B82-0BFA, U+200C-200D, U+20B9, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPi4UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0C00-0C7F, U+1CDA, U+1CF2, U+200C-200D, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiYUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0E01-0E5B, U+200C-200D, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPj0UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjwUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjIUvbQoi-E.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiIUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0308, U+0530-058F, U+2010, U+2024, U+25CC, U+FB13-FB17;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiAUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0980-09FE, U+1CD0, U+1CD2, U+1CD5-1CD6, U+1CD8, U+1CE1, U+1CEA, U+1CED, U+1CF2, U+1CF5-1CF7, U+200C-200D, U+20B9, U+25CC, U+A8F1;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPj8UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjYUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjMUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiMUvbQoi-Entw.woff2) format('woff2');unicode-range:U+1200-1399, U+2D80-2DDE, U+AB01-AB2E, U+1E7E0-1E7E6, U+1E7E8-1E7EB, U+1E7ED-1E7EE, U+1E7F0-1E7FE;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPi0UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0589, U+10A0-10FF, U+1C90-1CBA, U+1CBD-1CBF, U+2D00-2D2F;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjEUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPikUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0A80-0AFF, U+200C-200D, U+20B9, U+25CC, U+A830-A839;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPhEUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0A01-0A76, U+200C-200D, U+20B9, U+25CC, U+262C, U+A830-A839;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjAUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjkUvbQoi-Entw.woff2) format('woff2');unicode-range:U+1780-17FF, U+19E0-19FF, U+200C-200D, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjsUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0E81-0EDF, U+200C-200D, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPisUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0B01-0B77, U+1CDA, U+1CF2, U+200C-200D, U+20B9, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPi8UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0964-0965, U+0D81-0DF4, U+1CF2, U+200C-200D, U+25CC, U+111E1-111F4;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPlwUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8B1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA88, U+1FA90-1FABD, U+1FABF-1FAC5, U+1FACE-1FADB, U+1FAE0-1FAE8, U+1FAF0-1FAF8, U+1FB00-1FBFF;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiQUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0964-0965, U+0B82-0BFA, U+200C-200D, U+20B9, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPi4UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0951-0952, U+0964-0965, U+0C00-0C7F, U+1CDA, U+1CF2, U+200C-200D, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPiYUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0E01-0E5B, U+200C-200D, U+25CC;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPj0UvbQoi-Entw.woff2) format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjwUvbQoi-Entw.woff2) format('woff2');unicode-range:U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}@font-face{font-family:'Google Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/googlesans/v61/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjIUvbQoi-E.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}</style> <style type="text/css">@font-face{font-family:'Product Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/productsans/v19/pxiDypQkot1TnFhsFMOfGShVE9eOYktMqlap.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;}@font-face{font-family:'Product Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/productsans/v19/pxiDypQkot1TnFhsFMOfGShVFNeOYktMqlap.woff2) format('woff2');unicode-range:U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;}@font-face{font-family:'Product Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/productsans/v19/pxiDypQkot1TnFhsFMOfGShVGdeOYktMqlap.woff2) format('woff2');unicode-range:U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}@font-face{font-family:'Product Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/productsans/v19/pxiDypQkot1TnFhsFMOfGShVF9eOYktMqg.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}</style> <style>*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}:before,:after{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal}body{margin:0;line-height:inherit}*,:before,:after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(147 197 253 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }html{scroll-behavior:smooth}:root{font-family:Roboto,Helvetica Neue,sans-serif;font-size:16px;line-height:24px}:root,.light-scheme{--defaultcontainer-primary:26 115 232;--colorfulcontainer-primary:255 255 255;--canvascontainer-primary:25 103 210;--defaultcontainer-onPrimary:255 255 255;--colorfulcontainer-onPrimary:25 103 210;--canvascontainer-onPrimary:255 255 255;--defaultcontainer-secondary:64 84 193;--canvascontainer-secondary:255 255 255;--defaultcontainer-onSecondary:255 255 255;--canvascontainer-onSecondary:0 16 91;--defaultcontainer-secondaryContainer:150 165 255;--canvascontainer-secondaryContainer:64 84 193;--defaultcontainer-onSecondaryContainer:0 16 91;--canvascontainer-onSecondaryContainer:255 255 255;--defaultcontainer-background:255 255 255;--canvascontainer-background:232 234 246;--colorfulcontainer-background:232 234 246;--defaultcontainer-onBackground:27 27 31;--canvascontainer-onBackground:0 0 0;--colorfulcontainer-onBackground:255 255 255;--defaultcontainer-surface:255 255 255;--canvascontainer-surface:255 255 255;--defaultcontainer-onSurface:27 27 31;--canvascontainer-onSurface:27 27 31;--defaultcontainer-surfaceVariant:232 234 248;--canvascontainer-surfaceVariant:255 255 255;--defaultcontainer-onSurfaceVariant:92 94 102;--canvascontainer-onSurfaceVariant:92 94 102}</style><style>.mat-icon-button{font-size:24px!important}.mat-button.mat-button,.mat-flat-button.mat-flat-button{border-radius:8px;letter-spacing:.25px}.mat-flat-button:hover .mat-button-focus-overlay{opacity:.08}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}:before,:after{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal}body{margin:0;line-height:inherit}h1,h2,h3,h4{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}button{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button{text-transform:none}button,[type=button]{-webkit-appearance:button;background-color:transparent;background-image:none}dl,dd,h1,h2,h3,h4,p{margin:0}ul{list-style:none;margin:0;padding:0}button{cursor:pointer}img,svg{display:block;vertical-align:middle}img{max-width:100%;height:auto}*,:before,:after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(147 197 253 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%;margin-right:auto;margin-left:auto;padding-right:16px;padding-left:16px}@media (min-width: 664px){.container{max-width:664px;padding-right:24px;padding-left:24px}}@media (min-width: 984px){.container{max-width:984px;padding-right:24px;padding-left:24px}}@media (min-width: 1304px){.container{max-width:1304px;padding-right:24px;padding-left:24px}}.prose{color:rgb(var(--firex-onSurfaceVariant) / 1);max-width:65ch}.prose :where(a):not(:where([class~="not-prose"] *)){color:rgb(var(--firex-primary) / 1);text-decoration:none;font-weight:500}.prose :where(ul):not(:where([class~="not-prose"] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-left:1.625em}.prose :where(ul > li):not(:where([class~="not-prose"] *))::marker{color:var(--tw-prose-bullets)}.prose :where(h3):not(:where([class~="not-prose"] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h4):not(:where([class~="not-prose"] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(img):not(:where([class~="not-prose"] *)){margin-top:2em;margin-bottom:2em}.prose :where(code):not(:where([class~="not-prose"] *)){color:rgb(var(--firex-onSurface) / 1);font-weight:600;font-size:.875em;background-color:rgb(var(--firex-surface) / 1);border-radius:.25rem;padding:0 4px}.prose :where(code):not(:where([class~="not-prose"] *)):before{content:}.prose :where(code):not(:where([class~="not-prose"] *)):after{content:}.prose :where(a code):not(:where([class~="not-prose"] *)){color:inherit}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:rgb(0 0 0 / 50%);--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:14px;line-height:20px}.prose :where(p):not(:where([class~="not-prose"] *)){margin-top:16px;margin-bottom:16px}.prose :where(li):not(:where([class~="not-prose"] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ul > li):not(:where([class~="not-prose"] *)){padding-left:.375em}.prose :where(.prose > ul > li > *:first-child):not(:where([class~="not-prose"] *)){margin-top:1.25em}.prose :where(.prose > ul > li > *:last-child):not(:where([class~="not-prose"] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~="not-prose"] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(h3 + *):not(:where([class~="not-prose"] *)){margin-top:0}.prose :where(h4 + *):not(:where([class~="not-prose"] *)){margin-top:0}.prose :where(.prose > :first-child):not(:where([class~="not-prose"] *)){margin-top:0}.prose :where(.prose > :last-child):not(:where([class~="not-prose"] *)){margin-bottom:0}.prose :where(a[target="_blank"]):not(:where([class~="not-prose"] *)):after{background-color:currentColor;content:" ";display:inline-block;height:1em;margin-left:2px;-webkit-mask-image:url(/assets/icons/open_in_new.svg);mask-image:url(/assets/icons/open_in_new.svg);-webkit-mask-size:cover;mask-size:cover;vertical-align:middle;width:1em}.prose :where(dt):not(:where([class~="not-prose"] *)){font-weight:500;font-size:16px;line-height:24px}.prose :where(dd):not(:where([class~="not-prose"] *)){font-size:14px;line-height:20px}.prose :where(dd ~ dt):not(:where([class~="not-prose"] *)){margin-top:16px}.prose-base{font-size:1rem;line-height:1.75}.prose-base :where(p):not(:where([class~="not-prose"] *)){margin-top:1.25em;margin-bottom:1.25em}.prose-base :where(h3):not(:where([class~="not-prose"] *)){font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose-base :where(h4):not(:where([class~="not-prose"] *)){margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose-base :where(img):not(:where([class~="not-prose"] *)){margin-top:2em;margin-bottom:2em}.prose-base :where(code):not(:where([class~="not-prose"] *)){font-size:.875em}.prose-base :where(ul):not(:where([class~="not-prose"] *)){margin-top:1.25em;margin-bottom:1.25em;padding-left:1.625em}.prose-base :where(li):not(:where([class~="not-prose"] *)){margin-top:.5em;margin-bottom:.5em}.prose-base :where(ul > li):not(:where([class~="not-prose"] *)){padding-left:.375em}.prose-base :where(.prose-base > ul > li > *:first-child):not(:where([class~="not-prose"] *)){margin-top:1.25em}.prose-base :where(.prose-base > ul > li > *:last-child):not(:where([class~="not-prose"] *)){margin-bottom:1.25em}.prose-base :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~="not-prose"] *)){margin-top:.75em;margin-bottom:.75em}.prose-base :where(h3 + *):not(:where([class~="not-prose"] *)){margin-top:0}.prose-base :where(h4 + *):not(:where([class~="not-prose"] *)){margin-top:0}.prose-base :where(.prose-base > :first-child):not(:where([class~="not-prose"] *)){margin-top:0}.prose-base :where(.prose-base > :last-child):not(:where([class~="not-prose"] *)){margin-bottom:0}.fire-grid-flex{--col-width:56px;--col-gap:24px;gap:var(--col-gap);display:flex;flex-wrap:wrap;--padding-offset:calc(var(--col-width) * var(--col-start) + var(--col-gap) * var(--col-start));padding-left:var(--padding-offset)}.fire-grid-flex-main{width:calc(var(--padding-offset) + var(--col-width) * var(--main-col-count) + var(--col-gap) * max(var(--main-col-count) - 1,0))}.firex-chip-button{border-radius:50px!important;line-height:1em!important;padding:12px 20px!important}.relative{position:relative}.sticky{position:sticky}.top-0{top:0}.z-10{z-index:10}.mx-1{margin-left:.25rem;margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mb-4{margin-bottom:1rem}.mt-10{margin-top:2.5rem}.mt-14{margin-top:3.5rem}.mt-auto{margin-top:auto}.-mt-1{margin-top:-.25rem}.ml-1{margin-left:.25rem}.mt-5{margin-top:1.25rem}.ml-4{margin-left:1rem}.mr-5{margin-right:1.25rem}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.contents{display:contents}.hidden{display:none}.h-full{height:100%}.h-8{height:2rem}.w-full{width:100%}.w-\[0\.5px\]{width:.5px}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.\!cursor-pointer{cursor:pointer!important}.list-none{list-style-type:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-2{gap:.5rem}.gap-1{gap:.25rem}.gap-8{gap:2rem}.gap-4{gap:1rem}.self-center{align-self:center}.bg-background{--tw-bg-opacity:1;background-color:rgb(var(--firex-background) / var(--tw-bg-opacity))}.\!bg-surfaceVariant{--tw-bg-opacity:1 !important;background-color:rgb(var(--firex-surfaceVariant) / var(--tw-bg-opacity))!important}.bg-\[\#fbe9e7\]{--tw-bg-opacity:1;background-color:rgb(251 233 231 / var(--tw-bg-opacity))}.bg-surface{--tw-bg-opacity:1;background-color:rgb(var(--firex-surface) / var(--tw-bg-opacity))}.bg-\[\#F0F3FF\]{--tw-bg-opacity:1;background-color:rgb(240 243 255 / var(--tw-bg-opacity))}.bg-\[\#44474F8A\]{background-color:#44474f8a}.fill-\[\#C62828\]{fill:#c62828}.p-4{padding:1rem}.\!p-6{padding:1.5rem!important}.\!py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.\!px-3{padding-left:.75rem!important;padding-right:.75rem!important}.py-4{padding-top:1rem;padding-bottom:1rem}.\!py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.pb-12{padding-bottom:3rem}.pr-5{padding-right:1.25rem}.font-google{font-family:Google Sans,Helvetica Neue,sans-serif}.font-product{font-family:Product Sans,Helvetica Neue,sans-serif}.text-\[40px\]{font-size:40px}.text-lg{font-size:16px;line-height:24px}.text-xl{font-size:20px;line-height:28px}.text-md{font-size:14px;line-height:20px}.text-2xl{font-size:26px;line-height:32px}.\!font-normal{font-weight:400!important}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.text-primary{--tw-text-opacity:1;color:rgb(var(--firex-primary) / var(--tw-text-opacity))}.text-onSurface{--tw-text-opacity:1;color:rgb(var(--firex-onSurface) / var(--tw-text-opacity))}.text-onSurfaceVariant{--tw-text-opacity:1;color:rgb(var(--firex-onSurfaceVariant) / var(--tw-text-opacity))}.\!text-onSurfaceVariant{--tw-text-opacity:1 !important;color:rgb(var(--firex-onSurfaceVariant) / var(--tw-text-opacity))!important}.text-\[\#C62828\]{--tw-text-opacity:1;color:rgb(198 40 40 / var(--tw-text-opacity))}.text-onBackground{--tw-text-opacity:1;color:rgb(var(--firex-onBackground) / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.\!shadow-none{--tw-shadow:0 0 #0000 !important;--tw-shadow-colored:0 0 #0000 !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.default-theme-container{--firex-primary:var(--defaultcontainer-primary);--firex-onPrimary:var(--defaultcontainer-onPrimary);--firex-primaryContainer:var(--defaultcontainer-primaryContainer);--firex-onPrimaryContainer:var(--defaultcontainer-onPrimaryContainer);--firex-secondary:var(--defaultcontainer-secondary);--firex-onSecondary:var(--defaultcontainer-onSecondary);--firex-secondaryContainer:var(--defaultcontainer-secondaryContainer);--firex-onSecondaryContainer:var(--defaultcontainer-onSecondaryContainer);--firex-error:var(--defaultcontainer-error);--firex-onError:var(--defaultcontainer-onError);--firex-errorContainer:var(--defaultcontainer-errorContainer);--firex-onErrorContainer:var(--defaultcontainer-onErrorContainer);--firex-background:var(--defaultcontainer-background);--firex-onBackground:var(--defaultcontainer-onBackground);--firex-surface:var(--defaultcontainer-surface);--firex-onSurface:var(--defaultcontainer-onSurface);--firex-surfaceVariant:var(--defaultcontainer-surfaceVariant);--firex-onSurfaceVariant:var(--defaultcontainer-onSurfaceVariant);--firex-outline:var(--defaultcontainer-outline);--firex-outlineVariant:var(--defaultcontainer-outlineVariant)}.canvas-theme-container{--firex-primary:var(--canvascontainer-primary);--firex-onPrimary:var(--canvascontainer-onPrimary);--firex-primaryContainer:var(--canvascontainer-primaryContainer);--firex-onPrimaryContainer:var(--canvascontainer-onPrimaryContainer);--firex-secondary:var(--canvascontainer-secondary);--firex-onSecondary:var(--canvascontainer-onSecondary);--firex-secondaryContainer:var(--canvascontainer-secondaryContainer);--firex-onSecondaryContainer:var(--canvascontainer-onSecondaryContainer);--firex-error:var(--canvascontainer-error);--firex-onError:var(--canvascontainer-onError);--firex-errorContainer:var(--canvascontainer-errorContainer);--firex-onErrorContainer:var(--canvascontainer-onErrorContainer);--firex-background:var(--canvascontainer-background);--firex-onBackground:var(--canvascontainer-onBackground);--firex-surface:var(--canvascontainer-surface);--firex-onSurface:var(--canvascontainer-onSurface);--firex-surfaceVariant:var(--canvascontainer-surfaceVariant);--firex-onSurfaceVariant:var(--canvascontainer-onSurfaceVariant);--firex-outline:var(--canvascontainer-outline);--firex-outlineVariant:var(--canvascontainer-outlineVariant)}.colorful-theme-container{--firex-primary:var(--colorfulcontainer-primary);--firex-onPrimary:var(--colorfulcontainer-onPrimary);--firex-primaryContainer:var(--colorfulcontainer-primaryContainer);--firex-onPrimaryContainer:var(--colorfulcontainer-onPrimaryContainer);--firex-secondary:var(--colorfulcontainer-secondary);--firex-onSecondary:var(--colorfulcontainer-onSecondary);--firex-secondaryContainer:var(--colorfulcontainer-secondaryContainer);--firex-onSecondaryContainer:var(--colorfulcontainer-onSecondaryContainer);--firex-error:var(--colorfulcontainer-error);--firex-onError:var(--colorfulcontainer-onError);--firex-errorContainer:var(--colorfulcontainer-errorContainer);--firex-onErrorContainer:var(--colorfulcontainer-onErrorContainer);--firex-background:var(--colorfulcontainer-background);--firex-onBackground:var(--colorfulcontainer-onBackground);--firex-surface:var(--colorfulcontainer-surface);--firex-onSurface:var(--colorfulcontainer-onSurface);--firex-surfaceVariant:var(--colorfulcontainer-surfaceVariant);--firex-onSurfaceVariant:var(--colorfulcontainer-onSurfaceVariant);--firex-outline:var(--colorfulcontainer-outline);--firex-outlineVariant:var(--colorfulcontainer-outlineVariant)}.\!font-sm{font-family:Roboto,Helvetica Neue,sans-serif!important;font-size:13px!important;line-height:20px!important}.font-md{font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;line-height:20px}.\!font-lg{font-family:Roboto,Helvetica Neue,sans-serif!important;font-size:16px!important;line-height:24px!important}.font-xl{font-family:Google Sans,Helvetica Neue,sans-serif;font-size:20px;line-height:28px}.\!font-xl{font-family:Google Sans,Helvetica Neue,sans-serif!important;font-size:20px!important;line-height:28px!important}.clip-circle{clip-path:circle(49% at 50%)}html{scroll-behavior:smooth}.prose-img\:border :is(:where(img):not(:where([class~="not-prose"] *))){border-width:1px}.prose-img\:border-\[\#F0F3FF\] :is(:where(img):not(:where([class~="not-prose"] *))){--tw-border-opacity:1;border-color:rgb(240 243 255 / var(--tw-border-opacity))}.prose-img\:px-16 :is(:where(img):not(:where([class~="not-prose"] *))){padding-left:4rem;padding-right:4rem}.prose-img\:py-6 :is(:where(img):not(:where([class~="not-prose"] *))){padding-top:1.5rem;padding-bottom:1.5rem}@media (min-width: 664px){.sm\:mt-6{margin-top:1.5rem}.sm\:mt-8{margin-top:2rem}.sm\:mt-7{margin-top:1.75rem}.sm\:inline-flex{display:inline-flex}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:\!firex-big-button{padding:20px 52px!important}.sm\:font-lg{font-family:Roboto,Helvetica Neue,sans-serif;font-size:16px;line-height:24px}.sm\:\!font-xl{font-family:Google Sans,Helvetica Neue,sans-serif!important;font-size:20px!important;line-height:28px!important}.sm\:font-2xl{font-family:Google Sans,Helvetica Neue,sans-serif;font-size:26px;line-height:32px}.sm\:font-3xl{font-family:Google Sans,Helvetica Neue,sans-serif;font-size:32px;line-height:40px}}@media (min-width: 984px){.md\:float-right{float:right}.md\:block{display:block}.md\:hidden{display:none}}@media (min-width: 1304px){.lg\:inline{display:inline}.lg\:hidden{display:none}}.mat-button,.mat-icon-button,.mat-flat-button{font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;font-weight:500}.mat-card{font-family:Roboto,Helvetica Neue,sans-serif}.mat-chip{font-size:14px;font-weight:500}.mat-ripple{overflow:hidden;position:relative}.mat-ripple:not(:empty){transform:translateZ(0)}.cdk-visually-hidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap;outline:0;-webkit-appearance:none;-moz-appearance:none;left:0}.mat-focus-indicator{position:relative}.mat-focus-indicator:before{inset:0;position:absolute;box-sizing:border-box;pointer-events:none;display:var(--mat-focus-indicator-display, none);border:var(--mat-focus-indicator-border-width, 3px) var(--mat-focus-indicator-border-style, solid) var(--mat-focus-indicator-border-color, transparent);border-radius:var(--mat-focus-indicator-border-radius, 4px)}.mat-focus-indicator:focus:before{content:""}.mat-mdc-focus-indicator{position:relative}.mat-mdc-focus-indicator:before{inset:0;position:absolute;box-sizing:border-box;pointer-events:none;display:var(--mat-mdc-focus-indicator-display, none);border:var(--mat-mdc-focus-indicator-border-width, 3px) var(--mat-mdc-focus-indicator-border-style, solid) var(--mat-mdc-focus-indicator-border-color, transparent);border-radius:var(--mat-mdc-focus-indicator-border-radius, 4px)}.mat-mdc-focus-indicator:focus:before{content:""}.mat-button,.mat-icon-button,.mat-flat-button{font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;font-weight:500}.mat-card{font-family:Roboto,Helvetica Neue,sans-serif}.mat-chip{font-size:14px;font-weight:500}:root{font-family:Roboto,Helvetica Neue,sans-serif;font-size:16px;line-height:24px}:root,.light-scheme{--defaultcontainer-primary:26 115 232;--colorfulcontainer-primary:255 255 255;--canvascontainer-primary:25 103 210;--defaultcontainer-onPrimary:255 255 255;--colorfulcontainer-onPrimary:25 103 210;--canvascontainer-onPrimary:255 255 255;--defaultcontainer-secondary:64 84 193;--canvascontainer-secondary:255 255 255;--defaultcontainer-onSecondary:255 255 255;--canvascontainer-onSecondary:0 16 91;--defaultcontainer-secondaryContainer:150 165 255;--canvascontainer-secondaryContainer:64 84 193;--defaultcontainer-onSecondaryContainer:0 16 91;--canvascontainer-onSecondaryContainer:255 255 255;--defaultcontainer-background:255 255 255;--canvascontainer-background:232 234 246;--colorfulcontainer-background:232 234 246;--defaultcontainer-onBackground:27 27 31;--canvascontainer-onBackground:0 0 0;--colorfulcontainer-onBackground:255 255 255;--defaultcontainer-surface:255 255 255;--canvascontainer-surface:255 255 255;--defaultcontainer-onSurface:27 27 31;--canvascontainer-onSurface:27 27 31;--defaultcontainer-surfaceVariant:232 234 248;--canvascontainer-surfaceVariant:255 255 255;--defaultcontainer-onSurfaceVariant:92 94 102;--canvascontainer-onSurfaceVariant:92 94 102}:root .mat-button,:root .mat-icon-button,.light-scheme .mat-button,.light-scheme .mat-icon-button{color:inherit;background:transparent}:root .mat-button-focus-overlay,.light-scheme .mat-button-focus-overlay{background:#000000}:root .mat-flat-button,.light-scheme .mat-flat-button{color:#1e293b;background-color:#fff}:root .mat-flat-button.mat-primary,.light-scheme .mat-flat-button.mat-primary{color:rgb(var(--firex-onPrimary)/1)}:root .mat-flat-button.mat-primary.mat-button-disabled,:root .mat-flat-button.mat-button-disabled.mat-button-disabled,.light-scheme .mat-flat-button.mat-primary.mat-button-disabled,.light-scheme .mat-flat-button.mat-button-disabled.mat-button-disabled{color:#94a3b8}:root .mat-flat-button.mat-primary,.light-scheme .mat-flat-button.mat-primary{background-color:rgb(var(--firex-primary)/1)}:root .mat-flat-button.mat-primary.mat-button-disabled,:root .mat-flat-button.mat-button-disabled.mat-button-disabled,.light-scheme .mat-flat-button.mat-primary.mat-button-disabled,.light-scheme .mat-flat-button.mat-button-disabled.mat-button-disabled{background-color:#94a3b861}:root .mat-flat-button:not([class*=mat-elevation-z]),.light-scheme .mat-flat-button:not([class*=mat-elevation-z]){box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f}:root .mat-card,.light-scheme .mat-card{background:white;color:#1e293b}:root .mat-card:not([class*=mat-elevation-z]),.light-scheme .mat-card:not([class*=mat-elevation-z]){box-shadow:0 2px 1px -1px #0003,0 1px 1px #00000024,0 1px 3px #0000001f}:root .mat-chip.mat-standard-chip,.light-scheme .mat-chip.mat-standard-chip{background-color:#e2e8f0;color:#1e293b}:root .mat-chip.mat-standard-chip:not(.mat-chip-disabled):active,.light-scheme .mat-chip.mat-standard-chip:not(.mat-chip-disabled):active{box-shadow:0 3px 3px -2px #0003,0 3px 4px #00000024,0 1px 8px #0000001f}:root .mat-chip.mat-standard-chip:after,.light-scheme .mat-chip.mat-standard-chip:after{background:#000000}:root .mat-mdc-tab,.light-scheme .mat-mdc-tab{background-color:transparent}:root .mat-mdc-tab:not(.mat-mdc-tab-disabled) .mdc-tab__text-label,.light-scheme .mat-mdc-tab:not(.mat-mdc-tab-disabled) .mdc-tab__text-label{color:#0009}:root .mat-mdc-tab:not(.mat-mdc-tab-disabled).mdc-tab--active .mdc-tab__text-label,.light-scheme .mat-mdc-tab:not(.mat-mdc-tab-disabled).mdc-tab--active .mdc-tab__text-label{color:rgb(var(--firex-primary)/1)}:root .mat-mdc-tab:not(.mat-mdc-tab-disabled) .mdc-tab-indicator__content--underline,.light-scheme .mat-mdc-tab:not(.mat-mdc-tab-disabled) .mdc-tab-indicator__content--underline{border-color:rgb(var(--firex-primary)/1)}:root .mdc-tab__ripple:before,.light-scheme .mdc-tab__ripple:before{background-color:rgb(var(--firex-primary)/1)}:root .mat-mdc-tab-header-pagination-chevron,.light-scheme .mat-mdc-tab-header-pagination-chevron{border-color:var(--mdc-theme-on-surface, #000)}:root .mat-mdc-tab-header .mdc-tab,.light-scheme .mat-mdc-tab-header .mdc-tab{height:48px}:root .mdc-tab,.light-scheme .mdc-tab{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--mdc-typography-button-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:var(--mdc-typography-button-font-size, .875rem);line-height:var(--mdc-typography-button-line-height, 2.25rem);font-weight:var(--mdc-typography-button-font-weight, 500);letter-spacing:var(--mdc-typography-button-letter-spacing, .0892857143em);-webkit-text-decoration:var(--mdc-typography-button-text-decoration, none);text-decoration:var(--mdc-typography-button-text-decoration, none);text-transform:var(--mdc-typography-button-text-transform, uppercase)}</style><link rel="stylesheet" href="styles.4d161a61bd8de67e.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.4d161a61bd8de67e.css"></noscript><style ng-transition="serverApp">[_nghost-sc243]{--col-start: 0;--main-col-count: 8}@media (min-width: 984px){[_nghost-sc243]{--col-start: 1;--main-col-count: 6}}@media (min-width: 1304px){[_nghost-sc243]{--col-start: 2;--main-col-count: 8}}mat-tab-group[_ngcontent-sc243]{--mdc-typography-button-letter-spacing: 0}mat-tab-group[_ngcontent-sc243] .mat-mdc-tab-header{border-bottom-color:transparent}mat-tab-group[_ngcontent-sc243] mat-tab-header{width:100%;margin-right:auto;margin-left:auto;padding-right:16px;padding-left:16px}@media (min-width: 664px){mat-tab-group[_ngcontent-sc243] mat-tab-header{max-width:664px;padding-right:24px;padding-left:24px}}@media (min-width: 984px){mat-tab-group[_ngcontent-sc243] mat-tab-header{max-width:984px;padding-right:24px;padding-left:24px}}@media (min-width: 1304px){mat-tab-group[_ngcontent-sc243] mat-tab-header{max-width:1304px;padding-right:24px;padding-left:24px}}mat-tab-group[_ngcontent-sc243] .mat-mdc-tab-label-container{--col-width: 56px;--col-gap: 24px;gap:var(--col-gap);display:flex;flex-wrap:wrap;--padding-offset: calc(var(--col-width) * var(--col-start) + var(--col-gap) * var(--col-start));padding-left:var(--padding-offset);margin-left:-1.5rem}mat-tab-group[_ngcontent-sc243] mat-tab-header.mat-mdc-tab-header-pagination-controls-enabled .mat-mdc-tab-label-container{margin-left:0}mat-tab-group[_ngcontent-sc243] .mat-tab-label-active{--tw-text-opacity: 1;color:rgb(var(--firex-primary) / var(--tw-text-opacity));opacity:1}mat-tab-group[_ngcontent-sc243] .mdc-tab{text-transform:none}@media (min-width: 984px){firex-extension-header[_ngcontent-sc243]{--main-col-count: 5}}@media (min-width: 1304px){firex-extension-header[_ngcontent-sc243]{--main-col-count: 7}}</style><meta name="title" content="Run Payments with Stripe | Firebase Extensions Hub"><meta property="og:title" content="Run Payments with Stripe | Firebase Extensions Hub"><meta property="og:image" content="https://storage.googleapis.com/firebase-extensions-icons/extension_icons/stripe/stripe_subscriptions_120@2x.png"><meta name="twitter:image" content="https://storage.googleapis.com/firebase-extensions-icons/extension_icons/stripe/stripe_subscriptions_120@2x.png"><meta name="twitter:card" content="summary_large_image"><meta name="description" content="Controls access to paid content by syncing your one-time and recurring payments with Firebase Authentication."><meta property="og:description" content="Controls access to paid content by syncing your one-time and recurring payments with Firebase Authentication."><style ng-transition="serverApp">[_nghost-sc44]{--grey-gradient: linear-gradient( 300.19deg, #dee2f0 65.31%, #dfe3f0 82.76%, #e8eaf6 96.43% );--header-color: var(--app-shell-header-color, #d9ddf3);--nacho-color: var(--app-shell-nacho-color, var(--grey-gradient));display:flex;height:100vh;flex-direction:column}.scrolled[_nghost-sc44] header[_ngcontent-sc44], .scrolled [_nghost-sc44] header[_ngcontent-sc44]{background:var(--header-color)}.shell[_ngcontent-sc44]{contain:paint}.shell[_ngcontent-sc44]:before{content:" ";-webkit-mask-image:url(/assets/img/header_nacho.svg);mask-image:url(/assets/img/header_nacho.svg);-webkit-mask-size:100% max(2300px,100%);mask-size:100% max(2300px,100%);background:var(--nacho-color);position:absolute;width:100vw;height:1000px}@media (min-width: 984px){.shell[_ngcontent-sc44]:after{content:" ";display:block;position:absolute;height:var(--app-shell-header-img-height);width:var(--app-shell-header-img-width);translate:var(--app-shell-header-img-translate);scale:var(--app-shell-header-img-scale);top:0;left:50%}}main[_ngcontent-sc44]{contain:paint;z-index:1}@media (min-width: 664px){main[_ngcontent-sc44]:before{content:" ";-webkit-mask-image:url(/assets/img/bg_nacho.svg);mask-image:url(/assets/img/bg_nacho.svg);-webkit-mask-size:contain;mask-size:contain;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;background:var(--grey-gradient);position:absolute;width:100vw;height:100vw;top:1050px;z-index:-1}}nav[_ngcontent-sc44] a[_ngcontent-sc44]:hover .mat-button-focus-overlay{opacity:.08}nav[_ngcontent-sc44] a.selected[_ngcontent-sc44]{--tw-bg-opacity: 1;background-color:rgb(var(--firex-secondaryContainer) / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(var(--firex-onSecondaryContainer) / var(--tw-text-opacity))}</style><style ng-transition="serverApp">.mat-button .mat-button-focus-overlay,.mat-icon-button .mat-button-focus-overlay{opacity:0}.mat-button:hover:not(.mat-button-disabled) .mat-button-focus-overlay,.mat-stroked-button:hover:not(.mat-button-disabled) .mat-button-focus-overlay{opacity:.04}@media(hover: none){.mat-button:hover:not(.mat-button-disabled) .mat-button-focus-overlay,.mat-stroked-button:hover:not(.mat-button-disabled) .mat-button-focus-overlay{opacity:0}}.mat-button,.mat-icon-button,.mat-stroked-button,.mat-flat-button{box-sizing:border-box;position:relative;-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0);display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible}.mat-button::-moz-focus-inner,.mat-icon-button::-moz-focus-inner,.mat-stroked-button::-moz-focus-inner,.mat-flat-button::-moz-focus-inner{border:0}.mat-button.mat-button-disabled,.mat-icon-button.mat-button-disabled,.mat-stroked-button.mat-button-disabled,.mat-flat-button.mat-button-disabled{cursor:default}.mat-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-button.cdk-program-focused .mat-button-focus-overlay,.mat-icon-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-icon-button.cdk-program-focused .mat-button-focus-overlay,.mat-stroked-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-stroked-button.cdk-program-focused .mat-button-focus-overlay,.mat-flat-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-flat-button.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-button::-moz-focus-inner,.mat-icon-button::-moz-focus-inner,.mat-stroked-button::-moz-focus-inner,.mat-flat-button::-moz-focus-inner{border:0}.mat-raised-button{box-sizing:border-box;position:relative;-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0);display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0, 0, 0);transition:background 400ms cubic-bezier(0.25, 0.8, 0.25, 1),box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1)}.mat-raised-button::-moz-focus-inner{border:0}.mat-raised-button.mat-button-disabled{cursor:default}.mat-raised-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-raised-button.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-raised-button::-moz-focus-inner{border:0}.mat-raised-button._mat-animation-noopable{transition:none !important;animation:none !important}.mat-stroked-button{border:1px solid currentColor;padding:0 15px;line-height:34px}.mat-stroked-button .mat-button-ripple.mat-ripple,.mat-stroked-button .mat-button-focus-overlay{top:-1px;left:-1px;right:-1px;bottom:-1px}.mat-fab{box-sizing:border-box;position:relative;-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0);display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0, 0, 0);transition:background 400ms cubic-bezier(0.25, 0.8, 0.25, 1),box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);min-width:0;border-radius:50%;width:56px;height:56px;padding:0;flex-shrink:0}.mat-fab::-moz-focus-inner{border:0}.mat-fab.mat-button-disabled{cursor:default}.mat-fab.cdk-keyboard-focused .mat-button-focus-overlay,.mat-fab.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-fab::-moz-focus-inner{border:0}.mat-fab._mat-animation-noopable{transition:none !important;animation:none !important}.mat-fab .mat-button-wrapper{padding:16px 0;display:inline-block;line-height:24px}.mat-mini-fab{box-sizing:border-box;position:relative;-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0);display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0, 0, 0);transition:background 400ms cubic-bezier(0.25, 0.8, 0.25, 1),box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);min-width:0;border-radius:50%;width:40px;height:40px;padding:0;flex-shrink:0}.mat-mini-fab::-moz-focus-inner{border:0}.mat-mini-fab.mat-button-disabled{cursor:default}.mat-mini-fab.cdk-keyboard-focused .mat-button-focus-overlay,.mat-mini-fab.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-mini-fab::-moz-focus-inner{border:0}.mat-mini-fab._mat-animation-noopable{transition:none !important;animation:none !important}.mat-mini-fab .mat-button-wrapper{padding:8px 0;display:inline-block;line-height:24px}.mat-icon-button{padding:0;min-width:0;width:40px;height:40px;flex-shrink:0;line-height:40px;border-radius:50%}.mat-icon-button i,.mat-icon-button .mat-icon{line-height:24px}.mat-button-ripple.mat-ripple,.mat-button-focus-overlay{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;border-radius:inherit}.mat-button-ripple.mat-ripple:not(:empty){transform:translateZ(0)}.mat-button-focus-overlay{opacity:0;transition:opacity 200ms cubic-bezier(0.35, 0, 0.25, 1),background-color 200ms cubic-bezier(0.35, 0, 0.25, 1)}._mat-animation-noopable .mat-button-focus-overlay{transition:none}.mat-button-ripple-round{border-radius:50%;z-index:1}.mat-button .mat-button-wrapper>*,.mat-flat-button .mat-button-wrapper>*,.mat-stroked-button .mat-button-wrapper>*,.mat-raised-button .mat-button-wrapper>*,.mat-icon-button .mat-button-wrapper>*,.mat-fab .mat-button-wrapper>*,.mat-mini-fab .mat-button-wrapper>*{vertical-align:middle}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button{display:inline-flex;justify-content:center;align-items:center;font-size:inherit;width:2.5em;height:2.5em}.mat-flat-button::before,.mat-raised-button::before,.mat-fab::before,.mat-mini-fab::before{margin:calc(calc(var(--mat-focus-indicator-border-width, 3px) + 2px) * -1)}.mat-stroked-button::before{margin:calc(calc(var(--mat-focus-indicator-border-width, 3px) + 3px) * -1)}.cdk-high-contrast-active .mat-button,.cdk-high-contrast-active .mat-flat-button,.cdk-high-contrast-active .mat-raised-button,.cdk-high-contrast-active .mat-icon-button,.cdk-high-contrast-active .mat-fab,.cdk-high-contrast-active .mat-mini-fab{outline:solid 1px}</style><style ng-transition="serverApp">[_nghost-sc228]{display:inline-flex}</style><style ng-transition="serverApp">[_nghost-sc231]{display:block}</style><style ng-transition="serverApp">.scrolled[_nghost-sc42] header[_ngcontent-sc42], .scrolled [_nghost-sc42] header[_ngcontent-sc42]{background:var(--header-color)}</style><style ng-transition="serverApp">[_nghost-sc32]{display:inline-flex}</style><style ng-transition="serverApp">[_nghost-sc33]{display:inline-flex}</style><style ng-transition="serverApp">.mat-card{transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);display:block;position:relative;padding:16px;border-radius:4px}.mat-card._mat-animation-noopable{transition:none !important;animation:none !important}.mat-card>.mat-divider-horizontal{position:absolute;left:0;width:100%}[dir=rtl] .mat-card>.mat-divider-horizontal{left:auto;right:0}.mat-card>.mat-divider-horizontal.mat-divider-inset{position:static;margin:0}[dir=rtl] .mat-card>.mat-divider-horizontal.mat-divider-inset{margin-right:0}.cdk-high-contrast-active .mat-card{outline:solid 1px}.mat-card-actions,.mat-card-subtitle,.mat-card-content{display:block;margin-bottom:16px}.mat-card-title{display:block;margin-bottom:8px}.mat-card-actions{margin-left:-8px;margin-right:-8px;padding:8px 0}.mat-card-actions-align-end{display:flex;justify-content:flex-end}.mat-card-image{width:calc(100% + 32px);margin:0 -16px 16px -16px;display:block;overflow:hidden}.mat-card-image img{width:100%}.mat-card-footer{display:block;margin:0 -16px -16px -16px}.mat-card-actions .mat-button,.mat-card-actions .mat-raised-button,.mat-card-actions .mat-stroked-button{margin:0 8px}.mat-card-header{display:flex;flex-direction:row}.mat-card-header .mat-card-title{margin-bottom:12px}.mat-card-header-text{margin:0 16px}.mat-card-avatar{height:40px;width:40px;border-radius:50%;flex-shrink:0;object-fit:cover}.mat-card-title-group{display:flex;justify-content:space-between}.mat-card-sm-image{width:80px;height:80px}.mat-card-md-image{width:112px;height:112px}.mat-card-lg-image{width:152px;height:152px}.mat-card-xl-image{width:240px;height:240px;margin:-8px}.mat-card-title-group>.mat-card-xl-image{margin:-8px 0 8px}@media(max-width: 599px){.mat-card-title-group{margin:0}.mat-card-xl-image{margin-left:0;margin-right:0}}.mat-card>:first-child,.mat-card-content>:first-child{margin-top:0}.mat-card>:last-child:not(.mat-card-footer),.mat-card-content>:last-child:not(.mat-card-footer){margin-bottom:0}.mat-card-image:first-child{margin-top:-16px;border-top-left-radius:inherit;border-top-right-radius:inherit}.mat-card>.mat-card-actions:last-child{margin-bottom:-8px;padding-bottom:0}.mat-card-actions:not(.mat-card-actions-align-end) .mat-button:first-child,.mat-card-actions:not(.mat-card-actions-align-end) .mat-raised-button:first-child,.mat-card-actions:not(.mat-card-actions-align-end) .mat-stroked-button:first-child{margin-left:0;margin-right:0}.mat-card-actions-align-end .mat-button:last-child,.mat-card-actions-align-end .mat-raised-button:last-child,.mat-card-actions-align-end .mat-stroked-button:last-child{margin-left:0;margin-right:0}.mat-card-title:not(:first-child),.mat-card-subtitle:not(:first-child){margin-top:-4px}.mat-card-header .mat-card-subtitle:not(:first-child){margin-top:-8px}.mat-card>.mat-card-xl-image:first-child{margin-top:-8px}.mat-card>.mat-card-xl-image:last-child{margin-bottom:-8px}</style><style ng-transition="serverApp">[_nghost-sc234]{display:block}</style><style ng-transition="serverApp">[_nghost-sc150], svg[_ngcontent-sc150]{display:inline}</style><style ng-transition="serverApp">.mat-chip{position:relative;box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0);border:none;-webkit-appearance:none;-moz-appearance:none}.mat-chip::before{margin:calc(calc(var(--mat-focus-indicator-border-width, 3px) + 2px) * -1)}.mat-standard-chip{transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);display:inline-flex;padding:7px 12px;border-radius:16px;align-items:center;cursor:default;min-height:32px;height:1px}.mat-standard-chip._mat-animation-noopable{transition:none !important;animation:none !important}.mat-standard-chip .mat-chip-remove{border:none;-webkit-appearance:none;-moz-appearance:none;padding:0;background:none}.mat-standard-chip .mat-chip-remove.mat-icon,.mat-standard-chip .mat-chip-remove .mat-icon{width:18px;height:18px;font-size:18px}.mat-standard-chip::after{top:0;left:0;right:0;bottom:0;position:absolute;border-radius:inherit;opacity:0;content:"";pointer-events:none;transition:opacity 200ms cubic-bezier(0.35, 0, 0.25, 1)}.mat-standard-chip:hover::after{opacity:.12}.mat-standard-chip:focus{outline:none}.mat-standard-chip:focus::after{opacity:.16}.cdk-high-contrast-active .mat-standard-chip{outline:solid 1px}.cdk-high-contrast-active .mat-standard-chip.mat-chip-selected{outline-width:3px}.mat-standard-chip.mat-chip-disabled::after{opacity:0}.mat-standard-chip.mat-chip-disabled .mat-chip-remove,.mat-standard-chip.mat-chip-disabled .mat-chip-trailing-icon{cursor:default}.mat-standard-chip.mat-chip-with-trailing-icon.mat-chip-with-avatar,.mat-standard-chip.mat-chip-with-avatar{padding-top:0;padding-bottom:0}.mat-standard-chip.mat-chip-with-trailing-icon.mat-chip-with-avatar{padding-right:8px;padding-left:0}[dir=rtl] .mat-standard-chip.mat-chip-with-trailing-icon.mat-chip-with-avatar{padding-left:8px;padding-right:0}.mat-standard-chip.mat-chip-with-trailing-icon{padding-top:7px;padding-bottom:7px;padding-right:8px;padding-left:12px}[dir=rtl] .mat-standard-chip.mat-chip-with-trailing-icon{padding-left:8px;padding-right:12px}.mat-standard-chip.mat-chip-with-avatar{padding-left:0;padding-right:12px}[dir=rtl] .mat-standard-chip.mat-chip-with-avatar{padding-right:0;padding-left:12px}.mat-standard-chip .mat-chip-avatar{width:24px;height:24px;margin-right:8px;margin-left:4px}[dir=rtl] .mat-standard-chip .mat-chip-avatar{margin-left:8px;margin-right:4px}.mat-standard-chip .mat-chip-remove,.mat-standard-chip .mat-chip-trailing-icon{width:18px;height:18px;cursor:pointer}.mat-standard-chip .mat-chip-remove,.mat-standard-chip .mat-chip-trailing-icon{margin-left:8px;margin-right:0}[dir=rtl] .mat-standard-chip .mat-chip-remove,[dir=rtl] .mat-standard-chip .mat-chip-trailing-icon{margin-right:8px;margin-left:0}.mat-chip-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;border-radius:inherit;overflow:hidden;transform:translateZ(0)}.mat-chip-list-wrapper{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;margin:-4px}.mat-chip-list-wrapper input.mat-input-element,.mat-chip-list-wrapper .mat-standard-chip{margin:4px}.mat-chip-list-stacked .mat-chip-list-wrapper{flex-direction:column;align-items:flex-start}.mat-chip-list-stacked .mat-chip-list-wrapper .mat-standard-chip{width:100%}.mat-chip-avatar{border-radius:50%;justify-content:center;align-items:center;display:flex;overflow:hidden;object-fit:cover}input.mat-chip-input{width:150px;margin:4px;flex:1 0 150px}</style><style ng-transition="serverApp">[_nghost-sc142]{display:flex}</style><style ng-transition="serverApp">[_nghost-sc140]{display:inline-flex}</style><style ng-transition="serverApp">.mdc-tab{min-width:90px;padding-right:24px;padding-left:24px;display:flex;flex:1 0 auto;justify-content:center;box-sizing:border-box;margin:0;padding-top:0;padding-bottom:0;border:none;outline:none;text-align:center;white-space:nowrap;cursor:pointer;-webkit-appearance:none;z-index:1}.mdc-tab::-moz-focus-inner{padding:0;border:0}.mdc-tab--min-width{flex:0 1 auto}.mdc-tab__content{display:flex;align-items:center;justify-content:center;height:inherit;pointer-events:none}.mdc-tab__text-label{transition:150ms color linear;display:inline-block;line-height:1;z-index:2}.mdc-tab__icon{transition:150ms color linear;z-index:2}.mdc-tab--stacked .mdc-tab__content{flex-direction:column;align-items:center;justify-content:center}.mdc-tab--stacked .mdc-tab__text-label{padding-top:6px;padding-bottom:4px}.mdc-tab--active .mdc-tab__text-label,.mdc-tab--active .mdc-tab__icon{transition-delay:100ms}.mdc-tab:not(.mdc-tab--stacked) .mdc-tab__icon+.mdc-tab__text-label{padding-left:8px;padding-right:0}[dir=rtl] .mdc-tab:not(.mdc-tab--stacked) .mdc-tab__icon+.mdc-tab__text-label,.mdc-tab:not(.mdc-tab--stacked) .mdc-tab__icon+.mdc-tab__text-label[dir=rtl]{padding-left:0;padding-right:8px}.mdc-tab-indicator .mdc-tab-indicator__content--underline{border-top-width:2px}.mdc-tab-indicator .mdc-tab-indicator__content--icon{height:34px;font-size:34px}.mdc-tab-indicator{display:flex;position:absolute;top:0;left:0;justify-content:center;width:100%;height:100%;pointer-events:none;z-index:1}.mdc-tab-indicator__content{transform-origin:left;opacity:0}.mdc-tab-indicator__content--underline{align-self:flex-end;box-sizing:border-box;width:100%;border-top-style:solid}.mdc-tab-indicator__content--icon{align-self:center;margin:0 auto}.mdc-tab-indicator--active .mdc-tab-indicator__content{opacity:1}.mdc-tab-indicator .mdc-tab-indicator__content{transition:250ms transform cubic-bezier(0.4, 0, 0.2, 1)}.mdc-tab-indicator--no-transition .mdc-tab-indicator__content{transition:none}.mdc-tab-indicator--fade .mdc-tab-indicator__content{transition:150ms opacity linear}.mdc-tab-indicator--active.mdc-tab-indicator--fade .mdc-tab-indicator__content{transition-delay:100ms}.mat-mdc-tab-ripple{position:absolute;top:0;left:0;bottom:0;right:0;pointer-events:none}.mat-mdc-tab.mdc-tab{height:48px;flex-grow:0}.mat-mdc-tab .mdc-tab__ripple::before{content:"";display:block;position:absolute;top:0;left:0;right:0;bottom:0;opacity:0;pointer-events:none}.mat-mdc-tab .mdc-tab__content{position:relative}.mat-mdc-tab:hover .mdc-tab__ripple::before{opacity:.04}.mat-mdc-tab.cdk-program-focused .mdc-tab__ripple::before,.mat-mdc-tab.cdk-keyboard-focused .mdc-tab__ripple::before{opacity:.12}.mat-mdc-tab .mat-ripple-element{opacity:.12}.mat-mdc-tab-group.mat-mdc-tab-group-stretch-tabs>.mat-mdc-tab-header .mat-mdc-tab{flex-grow:1}.mat-mdc-tab-disabled{opacity:.4;pointer-events:none}.mat-mdc-tab-group{display:flex;flex-direction:column;max-width:100%}.mat-mdc-tab-group.mat-mdc-tab-group-inverted-header{flex-direction:column-reverse}.mat-mdc-tab-group.mat-mdc-tab-group-inverted-header .mdc-tab-indicator__content--underline{align-self:flex-start}.mat-mdc-tab-body-wrapper{position:relative;overflow:hidden;display:flex;transition:height 500ms cubic-bezier(0.35, 0, 0.25, 1)}.mat-mdc-tab-body-wrapper._mat-animation-noopable{transition:none !important;animation:none !important}</style><style ng-transition="serverApp">.note-from-firebase[_ngcontent-sc230] + p[_ngcontent-sc230]{margin-top:.5rem}</style><style ng-transition="serverApp">.mat-mdc-tab-header{display:flex;overflow:hidden;position:relative;flex-shrink:0}.mat-mdc-tab-header-pagination{-webkit-user-select:none;user-select:none;position:relative;display:none;justify-content:center;align-items:center;min-width:32px;cursor:pointer;z-index:2;-webkit-tap-highlight-color:rgba(0,0,0,0);touch-action:none;box-sizing:content-box;background:none;border:none;outline:0;padding:0}.mat-mdc-tab-header-pagination::-moz-focus-inner{border:0}.mat-mdc-tab-header-pagination .mat-ripple-element{opacity:.12}.mat-mdc-tab-header-pagination-controls-enabled .mat-mdc-tab-header-pagination{display:flex}.mat-mdc-tab-header-pagination-before,.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-after{padding-left:4px}.mat-mdc-tab-header-pagination-before .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-after .mat-mdc-tab-header-pagination-chevron{transform:rotate(-135deg)}.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-before,.mat-mdc-tab-header-pagination-after{padding-right:4px}.mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-before .mat-mdc-tab-header-pagination-chevron,.mat-mdc-tab-header-pagination-after .mat-mdc-tab-header-pagination-chevron{transform:rotate(45deg)}.mat-mdc-tab-header-pagination-chevron{border-style:solid;border-width:2px 2px 0 0;height:8px;width:8px}.mat-mdc-tab-header-pagination-disabled{box-shadow:none;cursor:default;pointer-events:none}.mat-mdc-tab-header-pagination-disabled .mat-mdc-tab-header-pagination-chevron{opacity:.4}.mat-mdc-tab-list{flex-grow:1;position:relative;transition:transform 500ms cubic-bezier(0.35, 0, 0.25, 1)}._mat-animation-noopable .mat-mdc-tab-list{transition:none}._mat-animation-noopable span.mdc-tab-indicator__content,._mat-animation-noopable span.mdc-tab__text-label{transition:none}.mat-mdc-tab-label-container{display:flex;flex-grow:1;overflow:hidden;z-index:1}.mat-mdc-tab-labels{display:flex;flex:1 0 auto}[mat-align-tabs=center]>.mat-mdc-tab-header .mat-mdc-tab-labels{justify-content:center}[mat-align-tabs=end]>.mat-mdc-tab-header .mat-mdc-tab-labels{justify-content:flex-end}.mat-mdc-tab::before{margin:5px}.cdk-high-contrast-active .mat-mdc-tab[aria-disabled=true]{color:GrayText}</style><style ng-transition="serverApp">firex-resource-icon[_ngcontent-sc240]{filter:brightness(.5) grayscale(1)}dl[_ngcontent-sc240] + h2[_ngcontent-sc240], label[_ngcontent-sc240] + h2[_ngcontent-sc240]{margin-top:1.5rem}</style><style ng-transition="serverApp">img[_ngcontent-sc239]{height:1em;width:1em}</style><style ng-transition="serverApp">.mat-mdc-tab-body{top:0;left:0;right:0;bottom:0;position:absolute;display:block;overflow:hidden;outline:0;flex-basis:100%}.mat-mdc-tab-body.mat-mdc-tab-body-active{position:relative;overflow-x:hidden;overflow-y:auto;z-index:1;flex-grow:1}.mat-mdc-tab-group.mat-mdc-tab-group-dynamic-height .mat-mdc-tab-body.mat-mdc-tab-body-active{overflow-y:hidden}.mat-mdc-tab-body-content{height:100%;overflow:auto}.mat-mdc-tab-group-dynamic-height .mat-mdc-tab-body-content{overflow:hidden}.mat-mdc-tab-body-content[style*="visibility: hidden"]{display:none}</style><style ng-transition="serverApp">.mat-divider{display:block;margin:0;border-top-width:1px;border-top-style:solid}.mat-divider.mat-divider-vertical{border-top:0;border-right-width:1px;border-right-style:solid}.mat-divider.mat-divider-inset{margin-left:80px}[dir=rtl] .mat-divider.mat-divider-inset{margin-left:auto;margin-right:80px}</style><style ng-transition="serverApp">[_nghost-sc225], svg[_ngcontent-sc225]{display:inline}</style></head> <body class="light-scheme default-container"> <firex-root ng-version="14.2.12" ng-server-context="other"><router-outlet></router-outlet><firex-extension _nghost-sc243=""><firex-app-shell _ngcontent-sc243="" _nghost-sc44=""><div _ngcontent-sc44="" class="canvas-theme-container flex-1 shell"><div _ngcontent-sc44="" class="bg-background h-full"><div _ngcontent-sc44="" class="colorful-theme-container contents"><firex-app-shell-header _ngcontent-sc44="" _nghost-sc42="" class="canvas-theme-container"><div _ngcontent-sc42=""></div><header _ngcontent-sc42="" class="sticky top-0 gap-4 z-10 py-4 text-onBackground transition-colors"><div _ngcontent-sc42="" class="flex grid-container items-start container"><firex-app-shell-logo _ngcontent-sc42="" class="flex flex-1 text-onBackground"><a href="/" class="flex items-start font-product"><img width="36" height="36" class="inline -mt-1" loading="eager" fetchpriority="high" src="/assets/img/firebase.svg"><span class="flex-1 inline-flex flex-wrap items-baseline gap-1"><span class="text-2xl"> Firebase Extensions Hub </span></span></a></firex-app-shell-logo><span _ngcontent-sc42="" class="hidden sm:inline-flex items-center"><nav _ngcontent-sc44="" firexappshellheadernav="" class="hidden sm:inline-flex justify-end mr-2" style="grid-area: nav;"><ul _ngcontent-sc44="" class="flex list-none gap-2 items-center"><li _ngcontent-sc44=""><a _ngcontent-sc44="" mat-button="" class="mat-focus-indicator firex-chip-button uppercase mat-button mat-button-base _mat-animation-noopable mat-flat-button selected" aria-disabled="false" href="/extensions"><span class="mat-button-wrapper">All Extensions</span><span matripple="" class="mat-ripple mat-button-ripple"></span><span class="mat-button-focus-overlay"></span></a></li><li _ngcontent-sc44="" class="hidden lg:inline"><a _ngcontent-sc44="" mat-button="" class="mat-focus-indicator firex-chip-button uppercase mat-button mat-button-base _mat-animation-noopable" aria-disabled="false" rel="noopener" target="_blank" href="https://firebase.google.com/docs/extensions"><span class="mat-button-wrapper"><span _ngcontent-sc44="" class="inline-flex gap-1">Docs <firex-icon-open-in-new _ngcontent-sc44="" _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></span></span><span matripple="" class="mat-ripple mat-button-ripple"></span><span class="mat-button-focus-overlay"></span></a></li><li _ngcontent-sc44="" class="hidden lg:inline"><a _ngcontent-sc44="" mat-button="" class="mat-focus-indicator firex-chip-button uppercase mat-button mat-button-base _mat-animation-noopable" aria-disabled="false" rel="noopener" target="_blank" href="https://console.firebase.google.com"><span class="mat-button-wrapper"><span _ngcontent-sc44="" class="inline-flex gap-1"> Firebase Console <firex-icon-open-in-new _ngcontent-sc44="" _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></span></span><span matripple="" class="mat-ripple mat-button-ripple"></span><span class="mat-button-focus-overlay"></span></a></li><li _ngcontent-sc44="" class="hidden lg:inline"><a _ngcontent-sc44="" mat-flat-button="" color="primary" class="mat-focus-indicator mat-flat-button mat-button-base mat-primary _mat-animation-noopable" aria-disabled="false"><span class="mat-button-wrapper"><span _ngcontent-sc44="" class="inline-flex gap-1 uppercase"> Build an extension </span></span><span matripple="" class="mat-ripple mat-button-ripple"></span><span class="mat-button-focus-overlay"></span></a></li></ul></nav></span><span _ngcontent-sc42="" class="lg:hidden inline-flex items-center"><button _ngcontent-sc44="" firexappshellheaderaction="" mat-icon-button="" class="mat-focus-indicator text-[40px] mat-icon-button mat-button-base _mat-animation-noopable"><span class="mat-button-wrapper"><div _ngcontent-sc44="" class="flex justify-center"><firex-icon-menu _ngcontent-sc44="" _nghost-sc32=""><svg _ngcontent-sc32="" xmlns="http://www.w3.org/2000/svg" width="1em" fill="currentColor" viewBox="0 0 24 24"><path _ngcontent-sc32="" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></svg></firex-icon-menu></div></span><span matripple="" class="mat-ripple mat-button-ripple mat-button-ripple-round"></span><span class="mat-button-focus-overlay"></span></button></span></div></header></firex-app-shell-header></div><main _ngcontent-sc44="" class="pb-12 relative h-full"><div _ngcontent-sc243="" class="ng-star-inserted"><div _ngcontent-sc243="" class="ng-star-inserted"><firex-deprecation-callout _ngcontent-sc243=""><div class="bg-[#fbe9e7] text-[#C62828] p-4 flex flex-wrap justify-center"><p class="font-medium container"><firex-icon-warning class="fill-[#C62828] mx-1" _nghost-sc225=""><svg _ngcontent-sc225="" xmlns="http://www.w3.org/2000/svg" viewBox="0 96 960 960" height="1.5em" width="1.5em" mattooltip="warning" class="mat-tooltip-trigger" aria-describedby="cdk-describedby-message-serverApp-77-79" cdk-describedby-host="serverApp-77"><path _ngcontent-sc225="" xmlns="http://www.w3.org/2000/svg" d="m40 936 440-760 440 760H40Zm444.175-117q12.825 0 21.325-8.675 8.5-8.676 8.5-21.5 0-12.825-8.675-21.325-8.676-8.5-21.5-8.5-12.825 0-21.325 8.675-8.5 8.676-8.5 21.5 0 12.825 8.675 21.325 8.676 8.5 21.5 8.5ZM454 708h60V484h-60v224Z"></path></svg><!----></firex-icon-warning><span class="ng-star-inserted"> This extension has been officially transferred by Stripe to <a class="text-primary underline inline-flex gap-0.5" href="https://extensions.dev/extensions/invertase/firestore-stripe-payments"> Invertase </a>, who will maintain the extension going forward. Please see <a class="text-primary underline inline-flex gap-0.5" href="https://github.com/invertase/stripe-firebase-extensions/issues/524">this issue</a> for more details. It is now recommended to uninstall this extension and install <a class="text-primary underline inline-flex gap-0.5" href="https://extensions.dev/extensions/invertase/firestore-stripe-payments"> invertase/firestore-stripe-payments </a> instead. </span><!----><!----><!----><a class="text-primary underline inline-flex gap-0.5 ng-star-inserted" href="https://github.com/stripe/stripe-firebase-extensions/tree/master/firestore-stripe-payments">Open in Github<firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a><!----></p></div></firex-deprecation-callout></div><!----></div><!----><div _ngcontent-sc243="" class="container mt-14"><a _ngcontent-sc243="" href="/extensions" mat-button="" class="mat-focus-indicator mat-button mat-button-base _mat-animation-noopable" aria-disabled="false"><span class="mat-button-wrapper"><div _ngcontent-sc243="" class="inline-flex items-center gap-1 text-primary"><firex-icon-arrow-back _ngcontent-sc243="" _nghost-sc228=""><svg _ngcontent-sc228="" xmlns="http://www.w3.org/2000/svg" width="1em" fill="currentColor" viewBox="0 0 24 24"><path _ngcontent-sc228="" d="m12 20-8-8 8-8 1.425 1.4-5.6 5.6H20v2H7.825l5.6 5.6Z"></path></svg></firex-icon-arrow-back>All extensions<!----></div></span><span matripple="" class="mat-ripple mat-button-ripple"></span><span class="mat-button-focus-overlay"></span></a></div><div _ngcontent-sc243="" class="container mt-4"><div _ngcontent-sc243="" class="fire-grid-flex"><firex-extension-header _ngcontent-sc243="" class="fire-grid-flex-main" _nghost-sc234=""><div _ngcontent-sc234="" class="flex gap-4 items-start sm:items-center flex-col sm:flex-row"><img _ngcontent-sc234="" class="clip-circle" width="84" height="84" loading="lazy" fetchpriority="auto" src="https://storage.googleapis.com/firebase-extensions-icons/extension_icons/stripe/stripe_subscriptions_120@2x.png"><!----><div _ngcontent-sc234="" class="flex flex-col gap-2 text-onSurface font-google"><h1 _ngcontent-sc234="" class="text-xl sm:font-3xl font-medium"> Run Payments with Stripe </h1><h2 _ngcontent-sc234="" class="text-lg sm:font-2xl"> Made by <a _ngcontent-sc234="" class="text-primary underline" href="/extensions?provider=stripe">Stripe</a><firex-icon-verified _ngcontent-sc234="" class="text-primary ml-1" _nghost-sc150=""><svg _ngcontent-sc150="" xmlns="http://www.w3.org/2000/svg" width="1em" fill="currentColor" viewBox="0 0 24 24" mattooltip="Publisher identity verified by Firebase" aria-label="Verified publisher" class="mat-tooltip-trigger" aria-describedby="cdk-describedby-message-serverApp-77-78" cdk-describedby-host="serverApp-77"><path _ngcontent-sc150="" xmlns="http://www.w3.org/2000/svg" d="m8.6 22.5-1.9-3.2-3.6-.8.35-3.7L1 12l2.45-2.8-.35-3.7 3.6-.8 1.9-3.2L12 2.95l3.4-1.45 1.9 3.2 3.6.8-.35 3.7L23 12l-2.45 2.8.35 3.7-3.6.8-1.9 3.2-3.4-1.45Zm2.35-6.95L16.6 9.9l-1.4-1.45-4.25 4.25-2.15-2.1L7.4 12Z"></path></svg><!----></firex-icon-verified><!----></h2></div></div><p _ngcontent-sc234="" class="text-google text-md sm:font-lg mt-5 sm:mt-8 text-onSurfaceVariant"> Controls access to paid content by syncing your one-time and recurring payments with Firebase Authentication. </p><div _ngcontent-sc234="" class="flex mt-4 sm:mt-7"><div _ngcontent-sc234="" class="flex-col pr-5 ml-4"><div _ngcontent-sc234="" class="flex justify-center"><span _ngcontent-sc234="" class="font-medium">14.9K+</span></div><div _ngcontent-sc234="">installs</div></div><div _ngcontent-sc234="" class="bg-[#44474F8A] w-[0.5px] h-8 mr-5 self-center"></div><mat-chip-list _ngcontent-sc234="" aria-label="Firebase products" class="mat-chip-list flex" id="mat-chip-list-39" tabindex="-1" aria-disabled="false" aria-invalid="false" aria-multiselectable="false" aria-orientation="horizontal"><div class="mat-chip-list-wrapper"><firex-category-chip _ngcontent-sc234="" class="!font-xl !font-normal flex items-center gap-1 self-center"><a href="/extensions?category=payments"><mat-chip class="mat-chip mat-focus-indicator !font-normal !font-sm !py-1 !px-3 !cursor-pointer !bg-surfaceVariant !text-onSurfaceVariant mat-primary mat-standard-chip !font-lg !py-2 _mat-animation-noopable" tabindex="-1" role="option" aria-disabled="false"><div class="mat-chip-ripple"></div><div class="flex gap-1"><firex-category-icon class="inline-flex text-lg text-xl" _nghost-sc142=""><!----><firex-icon-payments _ngcontent-sc142="" _nghost-sc140=""><svg _ngcontent-sc140="" xmlns="http://www.w3.org/2000/svg" width="1em" fill="currentColor" viewBox="0 0 24 24"><path _ngcontent-sc140="" d="M14 13q-1.25 0-2.125-.875T11 10q0-1.25.875-2.125T14 7q1.25 0 2.125.875T17 10q0 1.25-.875 2.125T14 13Zm-7 3q-.825 0-1.412-.588Q5 14.825 5 14V6q0-.825.588-1.412Q6.175 4 7 4h14q.825 0 1.413.588Q23 5.175 23 6v8q0 .825-.587 1.412Q21.825 16 21 16Zm2-2h10q0-.825.587-1.413Q20.175 12 21 12V8q-.825 0-1.413-.588Q19 6.825 19 6H9q0 .825-.588 1.412Q7.825 8 7 8v4q.825 0 1.412.587Q9 13.175 9 14Zm11 6H3q-.825 0-1.412-.587Q1 18.825 1 18V7h2v11h17ZM7 14V6v8Z"></path></svg></firex-icon-payments><!----><!----><!----><!----><!----><!----><!----><!----></firex-category-icon> Payments </div></mat-chip></a><!----><!----></firex-category-chip><!----></div></mat-chip-list><!----></div><!----><!----></firex-extension-header><!----><div _ngcontent-sc243="" class="flex-1"><a _ngcontent-sc243="" mat-flat-button="" color="primary" class="mat-focus-indicator sm:!font-xl sm:!firex-big-button md:float-right mat-flat-button mat-button-base mat-primary _mat-animation-noopable mat-button-disabled" disabled="true" tabindex="-1" aria-disabled="true" rel="noopener" target="_blank" href="https://console.firebase.google.com/project/_/extensions/install?ref=stripe/firestore-stripe-payments@0.3.4"><span class="mat-button-wrapper">Install in Firebase console</span><span matripple="" class="mat-ripple mat-button-ripple"></span><span class="mat-button-focus-overlay"></span></a></div><div _ngcontent-sc243="" class="w-full md:hidden sm:mt-6"><firex-extension-card _ngcontent-sc243="" _nghost-sc231=""><mat-card _ngcontent-sc231="" class="mat-card mat-focus-indicator !p-6 !shadow-none bg-[#F0F3FF] _mat-animation-noopable"><firex-extension-details _ngcontent-sc243=""><dl class="font-md prose"><dt> Works with </dt><dd> Authentication and Cloud Firestore </dd><!----><!----><!----><!----><dt> Version </dt><dd> 0.3.4 | <a rel="noopener" target="_blank" href="https://github.com/stripe/stripe-firebase-extensions/tree/107031923116d776ace0d33011a28d29e48fe827/firestore-stripe-payments">Source code</a><!----><!----></dd><!----><!----><!----><dt> License </dt><dd>Apache-2.0</dd><!----><!----><dt> Publisher </dt><dd>Stripe</dd><!----><!----><dt>Report</dt><dd><a href="https://github.com/stripe/stripe-firebase-extensions/issues" rel="noopener" target="_blank">Bug</a><!----></dd><dd><a href="https://docs.google.com/forms/d/e/1FAIpQLSfEUpeweFSW1aFwQ6WrXy64wyCyPbXxDwe7Td7nNYHkLBCeHA/viewform?entry.1623820757=stripe%2Ffirestore-stripe-payments" rel="noopener" target="_blank">Abuse</a></dd></dl><!----></firex-extension-details><!----></mat-card></firex-extension-card></div></div></div><mat-tab-group _ngcontent-sc243="" animationduration="0" class="mat-mdc-tab-group mt-6 sm:mt-8 mat-primary ng-star-inserted"><mat-tab-header class="mat-mdc-tab-header"><button aria-hidden="true" type="button" mat-ripple="" tabindex="-1" class="mat-ripple mat-mdc-tab-header-pagination mat-mdc-tab-header-pagination-before mat-mdc-tab-header-pagination-disabled" disabled=""><div class="mat-mdc-tab-header-pagination-chevron"></div></button><div class="mat-mdc-tab-label-container _mat-animation-noopable"><div role="tablist" class="mat-mdc-tab-list" style="transform: translateX(0px);"><div class="mat-mdc-tab-labels"><div role="tab" mattablabelwrapper="" cdkmonitorelementfocus="" class="mdc-tab mat-mdc-tab mat-mdc-focus-indicator mdc-tab--active ng-star-inserted mdc-tab-indicator--active" id="mat-tab-label-23-0" tabindex="0" aria-posinset="1" aria-setsize="5" aria-controls="mat-tab-content-23-0" aria-selected="true" aria-disabled="false"><span class="mdc-tab__ripple"></span><div mat-ripple="" class="mat-ripple mat-mdc-tab-ripple"></div><span class="mdc-tab__content"><span class="mdc-tab__text-label">Getting started<!----><!----></span><span class="mdc-tab-indicator"><span class="mdc-tab-indicator__content mdc-tab-indicator__content--underline"></span></span></span></div><div role="tab" mattablabelwrapper="" cdkmonitorelementfocus="" class="mdc-tab mat-mdc-tab mat-mdc-focus-indicator ng-star-inserted" id="mat-tab-label-23-1" tabindex="-1" aria-posinset="2" aria-setsize="5" aria-controls="mat-tab-content-23-1" aria-selected="false" aria-disabled="false"><span class="mdc-tab__ripple"></span><div mat-ripple="" class="mat-ripple mat-mdc-tab-ripple"></div><span class="mdc-tab__content"><span class="mdc-tab__text-label">Configurable parameters<!----><!----></span><span class="mdc-tab-indicator"><span class="mdc-tab-indicator__content mdc-tab-indicator__content--underline"></span></span></span></div><div role="tab" mattablabelwrapper="" cdkmonitorelementfocus="" class="mdc-tab mat-mdc-tab mat-mdc-focus-indicator ng-star-inserted" id="mat-tab-label-23-2" tabindex="-1" aria-posinset="3" aria-setsize="5" aria-controls="mat-tab-content-23-2" aria-selected="false" aria-disabled="false"><span class="mdc-tab__ripple"></span><div mat-ripple="" class="mat-ripple mat-mdc-tab-ripple"></div><span class="mdc-tab__content"><span class="mdc-tab__text-label">Resources created<!----><!----></span><span class="mdc-tab-indicator"><span class="mdc-tab-indicator__content mdc-tab-indicator__content--underline"></span></span></span></div><div role="tab" mattablabelwrapper="" cdkmonitorelementfocus="" class="mdc-tab mat-mdc-tab mat-mdc-focus-indicator ng-star-inserted" id="mat-tab-label-23-3" tabindex="-1" aria-posinset="4" aria-setsize="5" aria-controls="mat-tab-content-23-3" aria-selected="false" aria-disabled="false"><span class="mdc-tab__ripple"></span><div mat-ripple="" class="mat-ripple mat-mdc-tab-ripple"></div><span class="mdc-tab__content"><span class="mdc-tab__text-label">How to install<!----><!----></span><span class="mdc-tab-indicator"><span class="mdc-tab-indicator__content mdc-tab-indicator__content--underline"></span></span></span></div><div role="tab" mattablabelwrapper="" cdkmonitorelementfocus="" class="mdc-tab mat-mdc-tab mat-mdc-focus-indicator ng-star-inserted" id="mat-tab-label-23-4" tabindex="-1" aria-posinset="5" aria-setsize="5" aria-controls="mat-tab-content-23-4" aria-selected="false" aria-disabled="false"><span class="mdc-tab__ripple"></span><div mat-ripple="" class="mat-ripple mat-mdc-tab-ripple"></div><span class="mdc-tab__content"><span class="mdc-tab__text-label">Billing<!----><!----></span><span class="mdc-tab-indicator"><span class="mdc-tab-indicator__content mdc-tab-indicator__content--underline"></span></span></span></div><!----></div></div></div><button aria-hidden="true" type="button" mat-ripple="" tabindex="-1" class="mat-ripple mat-mdc-tab-header-pagination mat-mdc-tab-header-pagination-after mat-mdc-header-pagination-disabled" disabled=""><div class="mat-mdc-tab-header-pagination-chevron"></div></button></mat-tab-header><div class="mat-mdc-tab-body-wrapper _mat-animation-noopable"><mat-tab-body role="tabpanel" class="mat-mdc-tab-body ng-tns-c215-0 mat-mdc-tab-body-active ng-star-inserted" id="mat-tab-content-23-0" aria-labelledby="mat-tab-label-23-0"><div cdkscrollable="" class="mat-mdc-tab-body-content ng-tns-c215-0 ng-trigger ng-trigger-translateTab" style="transform:none;"><firex-extension-tab-content _ngcontent-sc243="" class="ng-star-inserted" style=""><div class="container mt-10"><div class="fire-grid-flex"><div class="fire-grid-flex-main"><firex-extension-card _ngcontent-sc243="" _nghost-sc231="" class="ng-star-inserted"><mat-card _ngcontent-sc231="" class="mat-card mat-focus-indicator !p-6 !shadow-none bg-[#F0F3FF] _mat-animation-noopable"><h2 _ngcontent-sc243="" class="font-xl font-medium mb-4"> How this extension works </h2><firex-markdown _ngcontent-sc243="" _nghost-sc227=""><div _ngcontent-sc227="" class="prose prose-base prose-img:border prose-img:border-[#F0F3FF] prose-img:px-16 prose-img:py-6 prose-pre:bg-[#F0F3FF] max-w-none ng-star-inserted"><p>Use this extension as a backend for your <a href="https://www.stripe.com/" rel="noopener noreferrer" target="_blank">Stripe</a> payments.</p> <p>The extension supports multiple use cases:</p> <ul> <li>Process one-time payments with <a href="https://stripe.com/docs/payments/checkout" rel="noopener noreferrer" target="_blank">Stripe Checkout</a> on the web.</li> <li>Create subscriptions for your users and manage access control via Firebase Authentication.</li> <li>Process payments &amp; set up payment methods with the mobile payment sheet on <a href="https://stripe.com/docs/payments/accept-a-payment?platform=android&amp;ui=payment-sheet" rel="noopener noreferrer" target="_blank">Android</a>, <a href="https://stripe.com/docs/payments/accept-a-payment?platform=ios&amp;ui=payment-sheet" rel="noopener noreferrer" target="_blank">iOS</a>, or with <a href="https://stripe.com/docs/payments/accept-a-payment?platform=react-native&amp;ui=payment-sheet" rel="noopener noreferrer" target="_blank">React Native</a>.</li> </ul> <h4 id="subscription-payments-with-stripe-checkout">Subscription payments with Stripe Checkout</h4> <p>Users can sign-up for your digital goods and paid content with Stripe Checkout and manage their subscriptions with the Stripe customer portal.</p> <p>This extension syncs customers’ subscription status with your Cloud Firestore and adds custom claims using Firebase Authentication for convenient access control in your application.</p> <p>The design for Stripe Checkout and the customer portal can be customized in your Stripe Dashboard <a href="https://dashboard.stripe.com/settings/branding" rel="noopener noreferrer" target="_blank">branding settings</a>. See this example which is customized to match the Firebase color scheme:</p> <p><img src="https://storage.googleapis.com/stripe-subscriptions-firebase-screenshots/firebase-stripe-subs-checkout.png" alt="Stripe Checkout Page"><br><img src="https://storage.googleapis.com/stripe-subscriptions-firebase-screenshots/firebase-stripe-subs-customer-portal.png" alt="Stripe Customer Portal"></p> <h4 id="recommended-usage">Recommended usage</h4> <p>If you’re building on the web platform, you can use this extension for any of your payment use cases. </p> <p>If you’re developing native mobile applications and you’re selling digital products or services within your app, (e.g. subscriptions, in-game currencies, game levels, access to premium content, or unlocking a full version), you must use the app store’s in-app purchase APIs. See <a href="https://developer.apple.com/app-store/review/guidelines/#payments" rel="noopener noreferrer" target="_blank">Apple’s</a> and <a href="https://support.google.com/googleplay/android-developer/answer/9858738?hl=en&amp;ref_topic=9857752" rel="noopener noreferrer" target="_blank">Google’s</a> guidelines for more information. </p> <p>For all other scenarios you can use the <a href="https://github.com/stripe/stripe-android" rel="noopener noreferrer" target="_blank">stripe-android</a>, <a href="https://github.com/stripe/stripe-ios" rel="noopener noreferrer" target="_blank">stripe-ios</a>, <a href="https://github.com/stripe/stripe-react-native" rel="noopener noreferrer" target="_blank">stripe-react-native</a>, or <a href="https://github.com/flutter-stripe/flutter_stripe" rel="noopener noreferrer" target="_blank">flutter_stripe</a> SDKs.</p> <h4 id="client-sdk">Client SDK</h4> <p>You can use the <a href="https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-web-sdk/README.md" rel="noopener noreferrer" target="_blank"><code>@stripe/firestore-stripe-payments</code></a><br>JavaScript package to easily access this extension from web clients. This client SDK provides<br>TypeScript type definitions and high-level convenience APIs for most common operations client<br>applications would want to implement using the extension.</p> <p>Use a package manager like NPM to install the above package, and use it in conjunction with<br>the Firebase Web SDK.</p> <h3 id="events">Events</h3> <p>This extension emits events, which allows you to listen to and run custom logic at different trigger points during the functioning of the extension. For example you can listen to events when a product has been added via the <code>product.created</code> event, or whenever a payment has succeeded through the <code>invoice.payment_succeeded</code> event.</p> <h4 id="additional-setup">Additional setup</h4> <p>Before installing this extension, set up the following Firebase services in your Firebase project:</p> <ul> <li><a href="https://firebase.google.com/docs/firestore" rel="noopener noreferrer" target="_blank">Cloud Firestore</a> to store customer &amp; subscription details.<ul> <li>Follow the steps in the <a href="https://firebase.google.com/docs/firestore/quickstart#create" rel="noopener noreferrer" target="_blank">documentation</a> to create a Cloud Firestore database.</li> </ul> </li> <li><a href="https://firebase.google.com/docs/auth" rel="noopener noreferrer" target="_blank">Firebase Authentication</a> to enable different sign-up options for your users.<ul> <li>Enable the sign-in methods in the <a href="https://console.firebase.google.com/project/_/authentication/providers" rel="noopener noreferrer" target="_blank">Firebase console</a> that you want to offer your users.</li> </ul> </li> </ul> <p>Then, in the <a href="https://dashboard.stripe.com" rel="noopener noreferrer" target="_blank">Stripe Dashboard</a>:</p> <ul> <li>Create a new <a href="https://stripe.com/docs/keys#limit-access" rel="noopener noreferrer" target="_blank">restricted key</a> with write access for the “Customers”, “Checkout Sessions” and “Customer portal” resources, and read-only access for the “Subscriptions” and “Prices” resources.</li> </ul> <h4 id="billing">Billing</h4> <p>This extension uses the following Firebase services which may have associated charges:</p> <ul> <li>Cloud Firestore</li> <li>Cloud Functions</li> <li>Cloud Secret Manager</li> <li>Firebase Authentication</li> <li>If you enable events <a href="https://cloud.google.com/eventarc/pricing" rel="noopener noreferrer" target="_blank">Eventarc fees apply</a>.</li> </ul> <p>This extension also uses the following third-party services:</p> <ul> <li>Stripe Payments (<a href="https://stripe.com/pricing" rel="noopener noreferrer" target="_blank">pricing information</a>)</li> <li>Stripe Billing (when using subscriptions. <a href="https://stripe.com/pricing#billing-pricing" rel="noopener noreferrer" target="_blank">pricing information</a>)</li> </ul> <p>You are responsible for any costs associated with your use of these services.</p> <h4 id="note-from-firebase">Note from Firebase</h4> <p>To install this extension, your Firebase project must be on the Blaze (pay-as-you-go) plan. You will only be charged for the resources you use. Most Firebase services offer a free tier for low-volume use. <a href="https://firebase.google.com/pricing" rel="noopener noreferrer" target="_blank">Learn more about Firebase billing.</a></p> <p>Starting August 17 2020, you will be billed a small amount (typically less than $0.10) when you install or reconfigure this extension. See the <a href="https://firebase.google.com/support/faq#expandable-15" rel="noopener noreferrer" target="_blank">Cloud Functions for Firebase billing FAQ</a> for a detailed explanation.</p> </div><!----><!----></firex-markdown></mat-card></firex-extension-card><!----></div><div class="flex-1 hidden md:block"><firex-extension-card _nghost-sc231=""><mat-card _ngcontent-sc231="" class="mat-card mat-focus-indicator !p-6 !shadow-none bg-[#F0F3FF] _mat-animation-noopable"><firex-extension-details><dl class="font-md prose ng-star-inserted"><dt class="ng-star-inserted"> Works with </dt><dd class="ng-star-inserted"> Authentication and Cloud Firestore </dd><!----><!----><!----><!----><dt class="ng-star-inserted"> Version </dt><dd class="ng-star-inserted"> 0.3.4 | <a rel="noopener" target="_blank" href="https://github.com/stripe/stripe-firebase-extensions/tree/107031923116d776ace0d33011a28d29e48fe827/firestore-stripe-payments" class="ng-star-inserted">Source code</a><!----><!----></dd><!----><!----><!----><dt class="ng-star-inserted"> License </dt><dd class="ng-star-inserted">Apache-2.0</dd><!----><!----><dt class="ng-star-inserted"> Publisher </dt><dd class="ng-star-inserted">Stripe</dd><!----><!----><dt>Report</dt><dd><a href="https://github.com/stripe/stripe-firebase-extensions/issues" rel="noopener" target="_blank" class="ng-star-inserted">Bug</a><!----></dd><dd><a href="https://docs.google.com/forms/d/e/1FAIpQLSfEUpeweFSW1aFwQ6WrXy64wyCyPbXxDwe7Td7nNYHkLBCeHA/viewform?entry.1623820757=stripe%2Ffirestore-stripe-payments" rel="noopener" target="_blank">Abuse</a></dd></dl><!----></firex-extension-details></mat-card></firex-extension-card><!----></div></div></div></firex-extension-tab-content><!----></div></mat-tab-body><mat-tab-body role="tabpanel" class="mat-mdc-tab-body ng-tns-c215-1 ng-star-inserted" id="mat-tab-content-23-1" aria-labelledby="mat-tab-label-23-1"><div cdkscrollable="" class="mat-mdc-tab-body-content ng-tns-c215-1 ng-trigger ng-trigger-translateTab" style="transform:translate3d(100% , 0 , 0);min-height:1px;visibility:hidden;"><!----></div></mat-tab-body><mat-tab-body role="tabpanel" class="mat-mdc-tab-body ng-tns-c215-2 ng-star-inserted" id="mat-tab-content-23-2" aria-labelledby="mat-tab-label-23-2"><div cdkscrollable="" class="mat-mdc-tab-body-content ng-tns-c215-2 ng-trigger ng-trigger-translateTab" style="transform:translate3d(100% , 0 , 0);min-height:1px;visibility:hidden;"><!----></div></mat-tab-body><mat-tab-body role="tabpanel" class="mat-mdc-tab-body ng-tns-c215-3 ng-star-inserted" id="mat-tab-content-23-3" aria-labelledby="mat-tab-label-23-3"><div cdkscrollable="" class="mat-mdc-tab-body-content ng-tns-c215-3 ng-trigger ng-trigger-translateTab" style="transform:translate3d(100% , 0 , 0);min-height:1px;visibility:hidden;"><!----></div></mat-tab-body><mat-tab-body role="tabpanel" class="mat-mdc-tab-body ng-tns-c215-4 ng-star-inserted" id="mat-tab-content-23-4" aria-labelledby="mat-tab-label-23-4"><div cdkscrollable="" class="mat-mdc-tab-body-content ng-tns-c215-4 ng-trigger ng-trigger-translateTab" style="transform:translate3d(100% , 0 , 0);min-height:1px;visibility:hidden;"><!----></div></mat-tab-body><!----></div></mat-tab-group><!----><!----></main></div></div><footer _ngcontent-sc44="" class="z-10"><firex-app-shell-footer _ngcontent-sc44=""><div class="default-theme-container bg-surface py-10 mt-auto"><div class="container flex flex-wrap justify-between gap-4 flex-col sm:flex-row"><firex-app-shell-logo class="text-onSurfaceVariant"><a href="/" class="flex items-start font-product"><img width="36" height="36" class="inline -mt-1" loading="eager" fetchpriority="high" src="/assets/img/firebase.svg"><span class="flex-1 inline-flex flex-wrap items-baseline gap-1"><span class="text-2xl"> Firebase Extensions Hub </span></span></a></firex-app-shell-logo><div class="flex gap-8 flex-col sm:flex-row"><div><h3 class="text-lg font-medium text-onSurface"> Google </h3><ul><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://policies.google.com/privacy">Privacy <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://policies.google.com/terms">Terms <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><!----></ul></div><div><h3 class="text-lg font-medium text-onSurface"> Stay connected </h3><ul><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://github.com/firebase/extensions">GitHub <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://twitter.com/Firebase">Twitter <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><!----></ul></div><div><h3 class="text-lg font-medium text-onSurface"> Learn </h3><ul><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://github.com/FirebaseExtended/karas-coffee">Sample app <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://firebase.google.com/docs/extensions">Docs <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><!----></ul></div><div><h3 class="text-lg font-medium text-onSurface"> Support </h3><ul><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://firebase.google.com/support">Contact support <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://groups.google.com/g/firebase-talk">Google group <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><li><a class="text-lg text-onSurfaceVariant flex gap-1" rel="noopener" target="_blank" href="https://docs.google.com/forms/d/e/1FAIpQLSfEUpeweFSW1aFwQ6WrXy64wyCyPbXxDwe7Td7nNYHkLBCeHA/viewform">Report abuse <firex-icon-open-in-new _nghost-sc33=""><svg _ngcontent-sc33="" xmlns="http://www.w3.org/2000/svg" width="1em" viewBox="0 0 24 24" fill="currentColor"><path _ngcontent-sc33="" d="M0 0h24v24H0V0z" fill="none"></path><path _ngcontent-sc33="" d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path></svg></firex-icon-open-in-new></a></li><!----></ul></div><!----></div></div></div></firex-app-shell-footer></footer></firex-app-shell><!----><!----><!----></firex-extension><!----></firex-root><!----> <script src="runtime.ca59241fe853c35d.js" type="module"></script><script src="polyfills.72b1618781c038db.js" type="module"></script><script src="main.4b8e8da7d14e656e.js" type="module"></script> <div style="visibility: hidden;" class="cdk-describedby-message-container cdk-visually-hidden" platform="server"><div id="cdk-describedby-message-serverApp-77-78" role="tooltip">Publisher identity verified by Firebase</div><div id="cdk-describedby-message-serverApp-77-79" role="tooltip">warning</div></div><script id="serverApp-state" type="application/json">{&q;extension-summary/stripe/firestore-stripe-payments&q;:{&q;ref&q;:&q;stripe/firestore-stripe-payments&q;,&q;publisherId&q;:&q;stripe&q;,&q;extensionId&q;:&q;firestore-stripe-payments&q;,&q;name&q;:&q;Run Payments with Stripe&q;,&q;publisher&q;:{&q;name&q;:&q;Stripe&q;,&q;link&q;:&q;&q;,&q;iconUri&q;:&q;https://storage.googleapis.com/firebase-extensions-icons/publisher_icons/stripe/stripe_author.svg&q;},&q;description&q;:&q;Controls access to paid content by syncing your one-time and recurring payments with Firebase Authentication.&q;,&q;iconUri&q;:&q;https://storage.googleapis.com/firebase-extensions-icons/extension_icons/stripe/stripe_subscriptions_120@2x.png&q;,&q;firebaseProducts&q;:[0,2],&q;categories&q;:[0],&q;collections&q;:[&q;store&q;],&q;tags&q;:[],&q;trending&q;:[],&q;activeInstallCount&q;:&q;14900&q;,&q;isDeprecated&q;:true,&q;isVerified&q;:true,&q;repoUri&q;:&q;https://github.com/stripe/stripe-firebase-extensions&q;,&q;latestApprovedVersion&q;:&q;0.3.4&q;},&q;extension-version/stripe/firestore-stripe-payments&q;:{&q;name&q;:&q;publishers/stripe/extensions/firestore-stripe-payments/versions/0.3.4&q;,&q;ref&q;:&q;stripe/firestore-stripe-payments@0.3.4&q;,&q;spec&q;:{&q;specVersion&q;:&q;v1beta&q;,&q;name&q;:&q;firestore-stripe-payments&q;,&q;version&q;:&q;0.3.4&q;,&q;description&q;:&q;Controls access to paid content by syncing your one-time and recurring payments with Firebase Authentication.&q;,&q;roles&q;:[{&q;role&q;:&q;firebaseauth.admin&q;,&q;reason&q;:&q;Allows the extension to set custom claims for users.&q;},{&q;role&q;:&q;datastore.user&q;,&q;reason&q;:&q;Allows the extension to store customers &a; subscriptions in Cloud Firestore.&q;}],&q;resources&q;:[{&q;name&q;:&q;createCustomer&q;,&q;type&q;:&q;firebaseextensions.v1beta.function&q;,&q;propertiesYaml&q;:&q;eventTrigger:\n eventType: providers/firebase.auth/eventTypes/user.create\n resource: projects/${PROJECT_ID}\nlocation: ${LOCATION}\nruntime: nodejs14\n&q;,&q;description&q;:&q;Creates a Stripe customer object when a new user signs up.&q;,&q;deletionPolicy&q;:&q;DELETE&q;,&q;properties&q;:{&q;eventTrigger&q;:{&q;eventType&q;:&q;providers/firebase.auth/eventTypes/user.create&q;,&q;resource&q;:&q;projects/${PROJECT_ID}&q;},&q;location&q;:&q;${LOCATION}&q;,&q;runtime&q;:&q;nodejs14&q;}},{&q;name&q;:&q;createCheckoutSession&q;,&q;type&q;:&q;firebaseextensions.v1beta.function&q;,&q;propertiesYaml&q;:&q;eventTrigger:\n eventType: providers/cloud.firestore/eventTypes/document.create\n resource: projects/${PROJECT_ID}/databases/(default)/documents/${CUSTOMERS_COLLECTION}/{uid}/checkout_sessions/{id}\nlocation: ${LOCATION}\nruntime: nodejs14\n&q;,&q;description&q;:&q;Creates a Checkout session to collect the customer&s;s payment details.&q;,&q;deletionPolicy&q;:&q;DELETE&q;,&q;properties&q;:{&q;eventTrigger&q;:{&q;eventType&q;:&q;providers/cloud.firestore/eventTypes/document.create&q;,&q;resource&q;:&q;projects/${PROJECT_ID}/databases/(default)/documents/${CUSTOMERS_COLLECTION}/{uid}/checkout_sessions/{id}&q;},&q;location&q;:&q;${LOCATION}&q;,&q;runtime&q;:&q;nodejs14&q;}},{&q;name&q;:&q;createPortalLink&q;,&q;type&q;:&q;firebaseextensions.v1beta.function&q;,&q;propertiesYaml&q;:&q;httpsTrigger: {}\nlocation: ${LOCATION}\nruntime: nodejs14\n&q;,&q;description&q;:&q;Creates links to the customer portal for the user to manage their payment &a; subscription details.&q;,&q;deletionPolicy&q;:&q;DELETE&q;,&q;properties&q;:{&q;httpsTrigger&q;:{},&q;location&q;:&q;${LOCATION}&q;,&q;runtime&q;:&q;nodejs14&q;}},{&q;name&q;:&q;handleWebhookEvents&q;,&q;type&q;:&q;firebaseextensions.v1beta.function&q;,&q;propertiesYaml&q;:&q;httpsTrigger: {}\nlocation: ${LOCATION}\nruntime: nodejs14\n&q;,&q;description&q;:&q;Handles Stripe webhook events to keep subscription statuses in sync and update custom claims.&q;,&q;deletionPolicy&q;:&q;DELETE&q;,&q;properties&q;:{&q;httpsTrigger&q;:{},&q;location&q;:&q;${LOCATION}&q;,&q;runtime&q;:&q;nodejs14&q;}},{&q;name&q;:&q;onUserDeleted&q;,&q;type&q;:&q;firebaseextensions.v1beta.function&q;,&q;propertiesYaml&q;:&q;eventTrigger:\n eventType: providers/firebase.auth/eventTypes/user.delete\n resource: projects/${PROJECT_ID}\nlocation: ${LOCATION}\nruntime: nodejs14\n&q;,&q;description&q;:&q;Deletes the Stripe customer object and cancels all their subscriptions when the user is deleted in Firebase Authentication.&q;,&q;deletionPolicy&q;:&q;DELETE&q;,&q;properties&q;:{&q;eventTrigger&q;:{&q;eventType&q;:&q;providers/firebase.auth/eventTypes/user.delete&q;,&q;resource&q;:&q;projects/${PROJECT_ID}&q;},&q;location&q;:&q;${LOCATION}&q;,&q;runtime&q;:&q;nodejs14&q;}},{&q;name&q;:&q;onCustomerDataDeleted&q;,&q;type&q;:&q;firebaseextensions.v1beta.function&q;,&q;propertiesYaml&q;:&q;eventTrigger:\n eventType: providers/cloud.firestore/eventTypes/document.delete\n resource: projects/${PROJECT_ID}/databases/(default)/documents/${CUSTOMERS_COLLECTION}/{uid}\nlocation: ${LOCATION}\nruntime: nodejs14\n&q;,&q;description&q;:&q;Deletes the Stripe customer object and cancels all their subscriptions when the customer doc in Cloud Firestore is deleted.&q;,&q;deletionPolicy&q;:&q;DELETE&q;,&q;properties&q;:{&q;eventTrigger&q;:{&q;eventType&q;:&q;providers/cloud.firestore/eventTypes/document.delete&q;,&q;resource&q;:&q;projects/${PROJECT_ID}/databases/(default)/documents/${CUSTOMERS_COLLECTION}/{uid}&q;},&q;location&q;:&q;${LOCATION}&q;,&q;runtime&q;:&q;nodejs14&q;}},{&q;type&q;:&q;secretmanager.googleapis.com/Secret&q;,&q;name&q;:&q;STRIPE_API_KEY&q;,&q;description&q;:&q;Stripe API key with restricted access&q;},{&q;type&q;:&q;secretmanager.googleapis.com/Secret&q;,&q;name&q;:&q;STRIPE_WEBHOOK_SECRET&q;,&q;description&q;:&q;Stripe webhook secret&q;},{&q;type&q;:&q;eventarc.googleapis.com/Channel&q;,&q;name&q;:&q;projects/${PROJECT}/locations/${REGION}/channels/firebase&q;,&q;description&q;:&q;When events are enabled, an Eventarc Channel is used to publish events and create event triggers.&q;}],&q;billingRequired&q;:true,&q;author&q;:{&q;authorName&q;:&q;Stripe&q;,&q;url&q;:&q;https://stripe.com&q;},&q;contributors&q;:[{&q;authorName&q;:&q;Thorsten Schaeff&q;,&q;url&q;:&q;https://twitter.com/thorwebdev&q;},{&q;authorName&q;:&q;Firebase&q;,&q;url&q;:&q;https://firebase.google.com&q;}],&q;license&q;:&q;Apache-2.0&q;,&q;releaseNotesUrl&q;:&q;https://github.com/stripe/stripe-firebase-extensions/tree/master/firestore-stripe-payments/CHANGELOG.md&q;,&q;sourceUrl&q;:&q;https://github.com/stripe/stripe-firebase-extensions/tree/master/firestore-stripe-payments&q;,&q;params&q;:[{&q;param&q;:&q;LOCATION&q;,&q;label&q;:&q;Cloud Functions deployment location&q;,&q;type&q;:&q;SELECT&q;,&q;description&q;:&q;Where do you want to deploy the functions created for this extension? You usually want a location close to your database. For help selecting a location, refer to the [location selection guide](https://firebase.google.com/docs/functions/locations).&q;,&q;required&q;:true,&q;options&q;:[{&q;value&q;:&q;us-central1&q;,&q;label&q;:&q;Iowa (us-central1)&q;},{&q;value&q;:&q;us-east1&q;,&q;label&q;:&q;South Carolina (us-east1)&q;},{&q;value&q;:&q;us-east4&q;,&q;label&q;:&q;Northern Virginia (us-east4)&q;},{&q;value&q;:&q;us-west1&q;,&q;label&q;:&q;Oregon (us-west1)&q;},{&q;value&q;:&q;us-west2&q;,&q;label&q;:&q;Los Angeles (us-west2)&q;},{&q;value&q;:&q;us-west3&q;,&q;label&q;:&q;Salt Lake City (us-west3)&q;},{&q;value&q;:&q;us-west4&q;,&q;label&q;:&q;Las Vegas (us-west4)&q;},{&q;value&q;:&q;europe-central2&q;,&q;label&q;:&q;Warsaw (europe-central2)&q;},{&q;value&q;:&q;europe-west1&q;,&q;label&q;:&q;Belgium (europe-west1)&q;},{&q;value&q;:&q;europe-west2&q;,&q;label&q;:&q;London (europe-west2)&q;},{&q;value&q;:&q;europe-west3&q;,&q;label&q;:&q;Frankfurt (europe-west3)&q;},{&q;value&q;:&q;europe-west6&q;,&q;label&q;:&q;Zurich (europe-west6)&q;},{&q;value&q;:&q;asia-east2&q;,&q;label&q;:&q;Hong Kong (asia-east2)&q;},{&q;value&q;:&q;asia-northeast1&q;,&q;label&q;:&q;Tokyo (asia-northeast1)&q;},{&q;value&q;:&q;asia-northeast2&q;,&q;label&q;:&q;Osaka (asia-northeast2)&q;},{&q;value&q;:&q;asia-northeast3&q;,&q;label&q;:&q;Seoul (asia-northeast3)&q;},{&q;value&q;:&q;asia-south1&q;,&q;label&q;:&q;Mumbai (asia-south1)&q;},{&q;value&q;:&q;asia-southeast2&q;,&q;label&q;:&q;Jakarta (asia-southeast2)&q;},{&q;value&q;:&q;northamerica-northeast1&q;,&q;label&q;:&q;Montreal (northamerica-northeast1)&q;},{&q;value&q;:&q;southamerica-east1&q;,&q;label&q;:&q;Sao Paulo (southamerica-east1)&q;},{&q;value&q;:&q;australia-southeast1&q;,&q;label&q;:&q;Sydney (australia-southeast1)&q;}],&q;default&q;:&q;us-central1&q;,&q;immutable&q;:true},{&q;param&q;:&q;PRODUCTS_COLLECTION&q;,&q;label&q;:&q;Products and pricing plans collection&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;What is the path to the Cloud Firestore collection where the extension should store Stripe pricing plans?&q;,&q;required&q;:true,&q;default&q;:&q;products&q;,&q;validationRegex&q;:&q;^[^/]+(/[^/]+/[^/]+)*$&q;,&q;validationErrorMessage&q;:&q;Firestore collection paths must be an odd number of segments separated by slashes, e.g. \&q;path/to/collection\&q;.&q;},{&q;param&q;:&q;CUSTOMERS_COLLECTION&q;,&q;label&q;:&q;Customer details and subscriptions collection&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;What is the path to the Cloud Firestore collection where the extension should store Stripe customer details? This can be the location of an existing user collection, the extension will not overwrite your existing data but rather merge the Stripe data into your existing `uid` docs.&q;,&q;required&q;:true,&q;default&q;:&q;customers&q;,&q;validationRegex&q;:&q;^[^/]+(/[^/]+/[^/]+)*$&q;,&q;validationErrorMessage&q;:&q;Firestore collection paths must be an odd number of segments separated by slashes, e.g. \&q;path/to/collection\&q;.&q;},{&q;param&q;:&q;STRIPE_CONFIG_COLLECTION&q;,&q;label&q;:&q;Stripe configuration collection&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;What is the path to the Cloud Firestore collection where the extension should store Stripe configuration?&q;,&q;default&q;:&q;configuration&q;,&q;validationRegex&q;:&q;^[^/]+(/[^/]+/[^/]+)*$&q;,&q;validationErrorMessage&q;:&q;Firestore collection paths must be an odd number of segments separated by slashes, e.g. \&q;path/to/collection\&q;.&q;},{&q;param&q;:&q;SYNC_USERS_ON_CREATE&q;,&q;label&q;:&q;Sync new users to Stripe customers and Cloud Firestore&q;,&q;type&q;:&q;SELECT&q;,&q;description&q;:&q;Do you want to automatically sync new users to customer objects in Stripe? If set to &s;Sync&s;, the extension will create a new customer object in Stripe and add a new doc to the customer collection in Firestore when a new user signs up via Firebase Authentication. If set to &s;Do not sync&s; (default), the extension will create the customer object \&q;on the fly\&q; with the first checkout session creation.&q;,&q;required&q;:true,&q;options&q;:[{&q;value&q;:&q;Do not sync&q;,&q;label&q;:&q;Do not sync&q;},{&q;value&q;:&q;Sync&q;,&q;label&q;:&q;Sync&q;}],&q;default&q;:&q;Do not sync&q;},{&q;param&q;:&q;DELETE_STRIPE_CUSTOMERS&q;,&q;label&q;:&q;Automatically delete Stripe customer objects&q;,&q;type&q;:&q;SELECT&q;,&q;description&q;:&q;Do you want to automatically delete customer objects in Stripe? When a user is deleted in Firebase Authentication or in Cloud Firestore and set to &s;Auto delete&s; the extension will delete their customer object in Stripe which will immediately cancel all subscriptions for the user.&q;,&q;required&q;:true,&q;options&q;:[{&q;value&q;:&q;Do not delete&q;,&q;label&q;:&q;Do not delete&q;},{&q;value&q;:&q;Auto delete&q;,&q;label&q;:&q;Auto delete&q;}],&q;default&q;:&q;Do not delete&q;},{&q;param&q;:&q;STRIPE_API_KEY&q;,&q;label&q;:&q;Stripe API key with restricted access&q;,&q;type&q;:&q;SECRET&q;,&q;description&q;:&q;What is your Stripe API key? We recommend creating a new [restricted key](https://stripe.com/docs/keys#limit-access) with write access only for the \&q;Customers\&q;, \&q;Checkout Sessions\&q; and \&q;Customer portal\&q; resources. And read-only access for the \&q;Subscriptions\&q; and \&q;Prices\&q; resources.&q;,&q;required&q;:true,&q;example&q;:&q;rk_live_1234567890&q;},{&q;param&q;:&q;STRIPE_WEBHOOK_SECRET&q;,&q;label&q;:&q;Stripe webhook secret&q;,&q;type&q;:&q;SECRET&q;,&q;description&q;:&q;This is your signing secret for a Stripe-registered webhook. This webhook can only be registered after installation. Leave this value untouched during installation, then follow the postinstall instructions for registering your webhook and configuring this value.&q;,&q;example&q;:&q;whsec_1234567890&q;},{&q;param&q;:&q;CREATE_CHECKOUT_SESSION_MIN_INSTANCES&q;,&q;label&q;:&q;Minimum instances for createCheckoutSession function&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;Set the minimum number of function instances that should be always be available to create Checkout Sessions. This number can be adjusted to reduce cold starts and increase the responsiveness of Checkout Session creation requests. Suggested values are 0 or 1. Please note this setting will likely incur billing costss, see the [Firebase documentation](https://firebase.google.com/docs/functions/manage-functions#reduce_the_number_of_cold_starts) for more information.&q;,&q;required&q;:true,&q;default&q;:&q;0&q;}],&q;preinstallContent&q;:&q;Use this extension as a backend for your [Stripe](https://www.stripe.com/) payments.\n\nThe extension supports multiple use cases:\n\n- Process one-time payments with [Stripe Checkout](https://stripe.com/docs/payments/checkout) on the web.\n- Create subscriptions for your users and manage access control via Firebase Authentication.\n- Process payments &a; set up payment methods with the mobile payment sheet on [Android](https://stripe.com/docs/payments/accept-a-payment?platform=android&a;ui=payment-sheet), [iOS](https://stripe.com/docs/payments/accept-a-payment?platform=ios&a;ui=payment-sheet), or with [React Native](https://stripe.com/docs/payments/accept-a-payment?platform=react-native&a;ui=payment-sheet).\n\n#### Subscription payments with Stripe Checkout\n\nUsers can sign-up for your digital goods and paid content with Stripe Checkout and manage their subscriptions with the Stripe customer portal.\n\nThis extension syncs customers&s; subscription status with your Cloud Firestore and adds custom claims using Firebase Authentication for convenient access control in your application.\n\nThe design for Stripe Checkout and the customer portal can be customized in your Stripe Dashboard [branding settings](https://dashboard.stripe.com/settings/branding). See this example which is customized to match the Firebase color scheme:\n\n![Stripe Checkout Page](https://storage.googleapis.com/stripe-subscriptions-firebase-screenshots/firebase-stripe-subs-checkout.png)\n![Stripe Customer Portal](https://storage.googleapis.com/stripe-subscriptions-firebase-screenshots/firebase-stripe-subs-customer-portal.png)\n\n#### Recommended usage\n\nIf you&s;re building on the web platform, you can use this extension for any of your payment use cases. \n\nIf you&s;re developing native mobile applications and you&s;re selling digital products or services within your app, (e.g. subscriptions, in-game currencies, game levels, access to premium content, or unlocking a full version), you must use the app store&s;s in-app purchase APIs. See [Apple&s;s](https://developer.apple.com/app-store/review/guidelines/#payments) and [Google&s;s](https://support.google.com/googleplay/android-developer/answer/9858738?hl=en&a;ref_topic=9857752) guidelines for more information. \n\nFor all other scenarios you can use the [stripe-android](https://github.com/stripe/stripe-android), [stripe-ios](https://github.com/stripe/stripe-ios), [stripe-react-native](https://github.com/stripe/stripe-react-native), or [flutter_stripe](https://github.com/flutter-stripe/flutter_stripe) SDKs.\n\n#### Client SDK\n\nYou can use the [`@stripe/firestore-stripe-payments`](https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-web-sdk/README.md)\nJavaScript package to easily access this extension from web clients. This client SDK provides\nTypeScript type definitions and high-level convenience APIs for most common operations client\napplications would want to implement using the extension.\n\nUse a package manager like NPM to install the above package, and use it in conjunction with\nthe Firebase Web SDK.\n\n### Events\n\nThis extension emits events, which allows you to listen to and run custom logic at different trigger points during the functioning of the extension. For example you can listen to events when a product has been added via the `product.created` event, or whenever a payment has succeeded through the `invoice.payment_succeeded` event.\n\n#### Additional setup\n\nBefore installing this extension, set up the following Firebase services in your Firebase project:\n\n- [Cloud Firestore](https://firebase.google.com/docs/firestore) to store customer &a; subscription details.\n - Follow the steps in the [documentation](https://firebase.google.com/docs/firestore/quickstart#create) to create a Cloud Firestore database.\n- [Firebase Authentication](https://firebase.google.com/docs/auth) to enable different sign-up options for your users.\n - Enable the sign-in methods in the [Firebase console](https://console.firebase.google.com/project/_/authentication/providers) that you want to offer your users.\n\nThen, in the [Stripe Dashboard](https://dashboard.stripe.com):\n\n- Create a new [restricted key](https://stripe.com/docs/keys#limit-access) with write access for the \&q;Customers\&q;, \&q;Checkout Sessions\&q; and \&q;Customer portal\&q; resources, and read-only access for the \&q;Subscriptions\&q; and \&q;Prices\&q; resources.\n\n#### Billing\n\nThis extension uses the following Firebase services which may have associated charges:\n\n- Cloud Firestore\n- Cloud Functions\n- Cloud Secret Manager\n- Firebase Authentication\n- If you enable events [Eventarc fees apply](https://cloud.google.com/eventarc/pricing).\n\nThis extension also uses the following third-party services:\n\n- Stripe Payments ([pricing information](https://stripe.com/pricing))\n- Stripe Billing (when using subscriptions. [pricing information](https://stripe.com/pricing#billing-pricing))\n\nYou are responsible for any costs associated with your use of these services.\n\n#### Note from Firebase\n\nTo install this extension, your Firebase project must be on the Blaze (pay-as-you-go) plan. You will only be charged for the resources you use. Most Firebase services offer a free tier for low-volume use. [Learn more about Firebase billing.](https://firebase.google.com/pricing)\n\nStarting August 17 2020, you will be billed a small amount (typically less than $0.10) when you install or reconfigure this extension. See the [Cloud Functions for Firebase billing FAQ](https://firebase.google.com/support/faq#expandable-15) for a detailed explanation.&q;,&q;postinstallContent&q;:&q;### Client SDK\n\nYou can use the [`@stripe/firestore-stripe-payments`](https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-web-sdk/README.md)\nJavaScript package to easily access this extension from web clients. This client SDK provides\nTypeScript type definitions and high-level convenience APIs for most common operations client\napplications would want to implement using the extension.\n\nUse a package manager like NPM to install the above package, and use it in conjunction with\nthe Firebase Web SDK.\n\n### Configuring the extension\n\nBefore you proceed, make sure you have the following Firebase services set up:\n\n- [Cloud Firestore](https://firebase.google.com/docs/firestore) to store customer &a; subscription details.\n - Follow the steps in the [documentation](https://firebase.google.com/docs/firestore/quickstart#create) to create a Cloud Firestore database.\n- [Firebase Authentication](https://firebase.google.com/docs/auth) to enable different sign-up options for your users.\n - Enable the sign-in methods in the [Firebase console](https://console.firebase.google.com/project/_/authentication/providers) that you want to offer your users.\n\n#### Set your Cloud Firestore security rules\n\nIt is crucial to limit data access to authenticated users only and for users to only be able to see their own information. For product and pricing information it is important to disable write access for client applications. Use the rules below to restrict access as recommended in your project&s;s [Cloud Firestore rules](https://console.firebase.google.com/project/${param:PROJECT_ID}/firestore/rules):\n\n```\nrules_version = &s;2&s;;\nservice cloud.firestore {\n match /databases/{database}/documents {\n match /${param:CUSTOMERS_COLLECTION}/{uid} {\n allow read: if request.auth.uid == uid;\n\n match /checkout_sessions/{id} {\n allow read, write: if request.auth.uid == uid;\n }\n match /subscriptions/{id} {\n allow read: if request.auth.uid == uid;\n }\n match /payments/{id} {\n allow read: if request.auth.uid == uid;\n }\n }\n\n match /${param:PRODUCTS_COLLECTION}/{id} {\n allow read: if true;\n\n match /prices/{id} {\n allow read: if true;\n }\n\n match /tax_rates/{id} {\n allow read: if true;\n }\n }\n }\n}\n```\n\n#### Configure Stripe webhooks\n\nYou need to set up a webhook that synchronizes relevant details from Stripe with your Cloud Firestore. This includes product and pricing data from the Stripe Dashboard, as well as customer&s;s subscription details.\n\nHere&s;s how to set up the webhook and configure your extension to use it:\n\n1. Configure your webhook:\n\n 1. Go to the [Stripe dashboard.](https://dashboard.stripe.com/webhooks)\n\n 1. Use the URL of your extension&s;s function as the endpoint URL. Here&s;s your function&s;s URL: `${function:handleWebhookEvents.url}`\n\n 1. Select the following events:\n\n - `product.created`\n - `product.updated`\n - `product.deleted`\n - `price.created`\n - `price.updated`\n - `price.deleted`\n - `checkout.session.completed`\n - `customer.subscription.created`\n - `customer.subscription.updated`\n - `customer.subscription.deleted`\n - `payment_intent.processing`\n - `payment_intent.succeeded`\n - `payment_intent.canceled`\n - `payment_intent.payment_failed`\n - `tax_rate.created` (optional)\n - `tax_rate.updated` (optional)\n - `invoice.paid` (optional, will sync invoices to Cloud Firestore)\n - `invoice.payment_succeeded` (optional, will sync invoices to Cloud Firestore)\n - `invoice.payment_failed` (optional, will sync invoices to Cloud Firestore)\n - `invoice.upcoming` (optional, will sync invoices to Cloud Firestore)\n - `invoice.marked_uncollectible` (optional, will sync invoices to Cloud Firestore)\n - `invoice.payment_action_required` (optional, will sync invoices to Cloud Firestore)\n\n1. Using the Firebase console or Firebase CLI, [reconfigure](https://console.firebase.google.com/project/${param:PROJECT_ID}/extensions/instances/${param:EXT_INSTANCE_ID}?tab=config) your extension with your webhook’s signing secret (such as, `whsec_12345678`). Enter the value in the parameter called `Stripe webhook secret`.\n\n#### Create product and pricing information (only required when building on the web platform)\n\nFor Stripe to automatically bill your users for recurring payments, you need to create your product and pricing information in the [Stripe Dashboard](https://dashboard.stripe.com/test/products). When you create or update your product and price information in the Stripe Dashboard these details are automatically synced with your Cloud Firestore, as long as the webhook is configured correctly as described above.\n\nThe extension currently supports pricing plans that bill a predefined amount at a specific interval. More complex plans (e.g. different pricing tiers or seats) are not yet supported. If you&s;d like to see support for these, please open a [feature request issue](https://github.com/stripe/stripe-firebase-extensions/issues/new/choose) with details about your business model and pricing plans.\n\nFor example, this extension works well for business models with different access level tiers, e.g.:\n\n- Product 1: Basic membership\n - Price 1: 10 USD per month\n - Price 2: 100 USD per year\n - Price 3: 8 GBP per month\n - Price 4: 80 GBP per year\n - [...]: additional currency and interval combinations\n- Product 2: Premium membership\n - Price 1: 20 USD per month\n - Price 2: 200 USD per year\n - Price 3: 16 GBP per month\n - Price 4: 160 GBP per year\n - [...]: additional currency and interval combinations\n\n#### Assign custom claim roles to products (only used for subscriptions)\n\nIf you want users to get assigned a [custom claim role](https://firebase.google.com/docs/auth/admin/custom-claims) to give them access to certain data when subscribed to a specific product, you can set a `firebaseRole` metadata value on the Stripe product ([see screenshot](https://www.gstatic.com/mobilesdk/200710_mobilesdk/ext_stripe_subscription_post_install.png)).\n\nThe value you set for `firebaseRole` (e.g. \&q;premium\&q; in the screenshot above) will be set as a custom claim `stripeRole` on the user. This allows you to [set specific security access rules](https://firebase.googleblog.com/2019/03/firebase-security-rules-admin-sdk-tips.html) based on the user&s;s roles, or [limit access to certain pages](https://firebase.google.com/docs/auth/admin/custom-claims#access_custom_claims_on_the_client). For example if you have one `basic` role and one `premium` role you could add the following to your Cloud Firestore rules:\n\n```\nrules_version = &s;2&s;;\nservice cloud.firestore {\n match /databases/{database}/documents {\n function hasBasicSubs() {\n return request.auth.token.stripeRole == \&q;basic\&q;;\n }\n\n function hasPremiumSubs() {\n return request.auth.token.stripeRole == \&q;premium\&q;;\n }\n\n match /content-basic/{doc} {\n allow read: if hasBasicSubs() || hasPremiumSubs();\n }\n match /content-premium/{doc} {\n allow read: if hasPremiumSubs();\n }\n }\n}\n```\n\nAlternatively you can validate their role client-side with the JavaScript SDK. When doing so you need to make sure to force-refresh the user token:\n\n```js\nasync function getCustomClaimRole() {\n await firebase.auth().currentUser.getIdToken(true);\n const decodedToken = await firebase.auth().currentUser.getIdTokenResult();\n return decodedToken.claims.stripeRole;\n}\n```\n\n#### Configure the Stripe customer portal (only used for subscriptions)\n\n1. Set your custom branding in the [settings](https://dashboard.stripe.com/settings/branding).\n1. Configure the Customer Portal [settings](https://dashboard.stripe.com/test/settings/billing/portal).\n1. Toggle on \&q;Allow customers to update their payment methods\&q;.\n1. Toggle on \&q;Allow customers to update subscriptions\&q;.\n1. Toggle on \&q;Allow customers to cancel subscriptions\&q;.\n1. Add the products and prices that you want to allow customer to switch between.\n1. Set up the required business information and links.\n\n### Using the extension\n\nOnce you&s;ve configured the extension you can add payments and access control to your websites and mobile apps fully client-side with the corresponding Firebase SDKs. You can experience a subscriptions demo application at [https://stripe-subs-ext.web.app](https://stripe-subs-ext.web.app/) and find the demo source code on [GitHub](https://github.com/stripe-samples/firebase-subscription-payments);\n\n#### Sign-up users with Firebase Authentication\n\nThe quickest way to sign-up new users is by using the [FirebaseUI library](https://firebase.google.com/docs/auth/web/firebaseui). Follow the steps outlined in the official docs. When configuring the extension you can choose to &s;Sync&s; new users to Stripe. If set to &s;Sync&s;, the extension listens to new users signing up and then automatically creates a Stripe customer object and a customer record in your Cloud Firestore. If set to &s;Do not sync&s; (default), the extension will create the customer object \&q;on the fly\&q; with the first checkout session creation.\n\n#### List available products and prices\n\nProducts and pricing information are normal collections and docs in your Cloud Firestore and can be queried as such:\n\n```js\ndb.collection(&s;${param:PRODUCTS_COLLECTION}&s;)\n .where(&s;active&s;, &s;==&s;, true)\n .get()\n .then(function (querySnapshot) {\n querySnapshot.forEach(async function (doc) {\n console.log(doc.id, &s; =&g; &s;, doc.data());\n const priceSnap = await doc.ref.collection(&s;prices&s;).get();\n priceSnap.docs.forEach((doc) =&g; {\n console.log(doc.id, &s; =&g; &s;, doc.data());\n });\n });\n });\n```\n\n### One-time payments on the web\n\nYou can create Checkout Sessions for one-time payments when referencing a one-time price ID. One-time payments will be synced to Cloud Firestore into a payments collection for the relevant customer doc if you update your webhook handler in the Stripe dashboard to include the following events: `payment_intent.succeeded`, `payment_intent.payment_failed`, `payment_intent.canceled`, `payment_intent.processing`.\n\nTo create a Checkout Session ID for a one-time payment, pass `mode: &s;payment` to the Checkout Session doc creation:\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser.uid)\n .collection(\&q;checkout_sessions\&q;)\n .add({\n mode: \&q;payment\&q;,\n price: \&q;price_1GqIC8HYgolSBA35zoTTN2Zl\&q;, // One-time price created in Stripe\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n### Mobile payments (with the mobile payment sheet on iOS and Android)\n\n#### One-time payments\n\nTo create a one time payment in your mobile application, create a new doc in your `${param:CUSTOMERS_COLLECTION}/{uid}/checkout_sessions` collection with the following parameters:\n\n- client: &s;mobile&s;\n- mode: &s;payment&s;\n- amount: [{payment amount}](https://stripe.com/docs/api/payment_intents/object#payment_intent_object-amount)\n- currency: [{currency code}](https://stripe.com/docs/api/payment_intents/object#payment_intent_object-currency)\n\nThen listen for the extension to append `paymentIntentClientSecret`, `ephemeralKeySecret`, and `customer` to the doc and use these to [integrate the mobile payment sheet](https://stripe.com/docs/payments/accept-a-payment?platform=ios&a;ui=payment-sheet#integrate-payment-sheet).\n\n#### Set up a payment method for future usage\n\nYou can collect a payment method from your customer to charge it at a later point in time. To do so create a new doc in your `${param:CUSTOMERS_COLLECTION}/{uid}/checkout_sessions` collection with the following parameters:\n\n- client: &s;mobile&s;\n- mode: &s;setup&s;\n\nThen listen for the extension to append `setupIntentClientSecret`, `ephemeralKeySecret`, and `customer` to the doc and use these to [integrate the mobile payment sheet](https://stripe.com/docs/payments/accept-a-payment?platform=ios&a;ui=payment-sheet#integrate-payment-sheet).\n\n### Subscription payments (web only)\n\n#### Start a subscription with Stripe Checkout\n\nTo subscribe the user to a specific pricing plan, create a new doc in the `checkout_sessions` collection for the user. The extension will update the doc with a Stripe Checkout session ID which you then use to redirect the user to the checkout page.\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser.uid)\n .collection(&s;checkout_sessions&s;)\n .add({\n price: &s;price_1GqIC8HYgolSBA35zoTTN2Zl&s;,\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n// Wait for the CheckoutSession to get attached by the extension\ndocRef.onSnapshot((snap) =&g; {\n const { error, url } = snap.data();\n if (error) {\n // Show an error to your customer and\n // inspect your Cloud Function logs in the Firebase console.\n alert(`An error occured: ${error.message}`);\n }\n if (url) {\n // We have a Stripe Checkout URL, let&s;s redirect.\n window.location.assign(url);\n }\n});\n```\n\n#### Handling trials\n\nBy default, the trial period days that you&s;ve specified on the pricing plan will be applied to the checkout session. Should you wish to not offer the trial for a certain user (e.g. they&s;ve previously had a subscription with a trial that they canceled and are now signing up again), you can specify `trial_from_plan: false` when creating the checkout session doc:\n\n```js\nconst docRef = await db\n .collection(\&q;${param:CUSTOMERS_COLLECTION}\&q;)\n .doc(currentUser)\n .collection(\&q;checkout_sessions\&q;)\n .add({\n price: \&q;price_1GqIC8HYgolSBA35zoTTN2Zl\&q;,\n trial_from_plan: false,\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n#### Applying discount, coupon, promotion codes\n\nYou can create customer-facing promotion codes in the [Stripe Dashboard](https://dashboard.stripe.com/coupons/create). Refer to the [docs](https://stripe.com/docs/billing/subscriptions/discounts/codes) for a detailed guide on how to set these up.\n\nIn order for the promotion code redemption box to show up on the checkout page, set `allow_promotion_codes: true` when creating the `checkout_sessions` document:\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser)\n .collection(&s;checkout_sessions&s;)\n .add({\n price: &s;price_1GqIC8HYgolSBA35zoTTN2Zl&s;,\n allow_promotion_codes: true,\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n#### Applying promotion codes programmatically\n\nYou can set a [promotion code](https://stripe.com/docs/billing/subscriptions/discounts/codes) to be applied to the checkout session without the customer needing to input it.\n\n**_NOTE_**: anyone with access to a promotion code ID would be able to apply it to their checkout session. Therefore make sure to limit your promotion codes and archive any codes you don&s;t want to offer anymore.\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser.uid)\n .collection(\&q;checkout_sessions\&q;)\n .add({\n promotion_code: \&q;promo_1HCrfVHYgolSBA35b1q98MNk\&q;,\n price: \&q;price_1GqIC8HYgolSBA35zoTTN2Zl\&q;,\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n#### Automatic tax calculation with [Stripe Tax](https://stripe.com/tax)\n\nStripe Tax lets you calculate and collect sales tax, VAT, and GST. Know where to register, automatically collect the right amount of tax, and access the reports you need to file returns.\n\n1. Request access: https://stripe.com/tax#request-access\n2. Set up Stripe Tax in the Dashboard: https://stripe.com/docs/tax/set-up\n3. Enable automatic tax calculation when creating your `checkout_sessions` docs:\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser.uid)\n .collection(\&q;checkout_sessions\&q;)\n .add({\n automatic_tax: true, // Automatically calculate tax based on the customer&s;s address\n tax_id_collection: true, // Collect the customer&s;s tax ID (important for B2B transactions)\n price: \&q;price_1GqIC8HYgolSBA35zoTTN2Zl\&q;,\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n#### Applying tax rates dynamically\n\nStripe Checkout supports applying the correct tax rate for customers in US, GB, AU, and all countries in the EU. With [dynamic tax rates](https://stripe.com/docs/billing/subscriptions/taxes#adding-tax-rates-to-checkout), you create tax rates for different regions (e.g., a 20% VAT tax rate for customers in the UK and a 7.25% sales tax rate for customers in California, US) and Stripe attempts to match your customer’s location to one of those tax rates.\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser)\n .collection(\&q;checkout_sessions\&q;)\n .add({\n line_items: [\n {\n price: \&q;price_1HCUD4HYgolSBA35icTHEXd5\&q;,\n quantity: 1,\n dynamic_tax_rates: [\&q;txr_1IJJtvHYgolSBA35ITTBOaew\&q;, \&q;txr_1Hlsk0HYgolSBA35rlraUVWO\&q;, \&q;txr_1HCshzHYgolSBA35WkPjzOOi\&q;],\n },\n ],\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n#### Applying static tax rates\n\nYou can collect and report taxes with [Tax Rates](https://stripe.com/docs/billing/taxes/tax-rates). To apply tax rates to the subscription, you first need to create your tax rates in the [Stripe Dashboard](https://dashboard.stripe.com/tax-rates). When creating a new `checkout_sessions` document, specify the optional `tax_rates` list with [up to five](https://stripe.com/docs/billing/taxes/tax-rates#using-multiple-tax-rates) tax rate IDs:\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser)\n .collection(&s;checkout_sessions&s;)\n .add({\n price: &s;price_1GqIC8HYgolSBA35zoTTN2Zl&s;,\n tax_rates: [&s;txr_1HCjzTHYgolSBA35m0e1tJN5&s;],\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n#### Collecting a shipping address during checkout\n\nTo collect a shipping address from your customer during checkout, you need to create a `shipping_countries` doc in your `products` collection. This doc needs to have a field called `allowed_countries` which needs to be an array. In this array, add the country codes for the countries that you ship to. You can find a list of supported countries [here](https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-shipping_address_collection-allowed_countries).\n\nSecondly, you need to add `collect_shipping_address: true` to the Checkout Session doc creation:\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser.uid)\n .collection(\&q;checkout_sessions\&q;)\n .add({\n collect_shipping_address: true,\n price: \&q;price_1GqIC8HYgolSBA35zoTTN2Zl\&q;,\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n#### Setting metadata on the subscription\n\nYou can optionally set a metadata object with key-value pairs when creating the checkout session. This can be useful for storing additional information about the customer&s;s subscription. This metadata will be synced to both the Stripe subscription object (making it searchable in the Stripe Dashboard) and the subscription document in the Cloud Firestore.\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser)\n .collection(&s;checkout_sessions&s;)\n .add({\n price: &s;price_1GqIC8HYgolSBA35zoTTN2Zl&s;,\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n metadata: {\n item: &s;item001&s;,\n },\n });\n```\n\n#### Adding multiple prices, including one-time setup fees\n\nIn addition to recurring prices, you can add one-time prices. These will only be on the initial invoice. This is useful for adding setup fees or other one-time fees associated with a subscription. To do so you will need to pass a `line_items` array instead:\n\n```js\nconst docRef = await db\n .collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser)\n .collection(&s;checkout_sessions&s;)\n .add({\n line_items: [\n {\n price: &s;price_1HCUD4HYgolSBA35icTHEXd5&s;, // RECURRING_PRICE_ID\n quantity: 1,\n tax_rates: [&s;txr_1HCjzTHYgolSBA35m0e1tJN5&s;],\n },\n {\n price: &s;price_1HEtgDHYgolSBA35LMkO3ExX&s;, // ONE_TIME_PRICE_ID\n quantity: 1,\n tax_rates: [&s;txr_1HCjzTHYgolSBA35m0e1tJN5&s;],\n },\n ],\n success_url: window.location.origin,\n cancel_url: window.location.origin,\n });\n```\n\n**_NOTE_**: If you specify more than one recurring price in the `line_items` array, the subscription object in Cloud Firestore will list all recurring prices in the `prices` array. The `price` attribute on the subscription in Cloud Firestore will be equal to the first item in the `prices` array: `price === prices[0]`.\n\nNote that the Stripe customer portal currently does not support changing subscriptions with multiple recurring prices. In this case the portal will only offer the option to cancel the subscription.\n\n#### Start a subscription via the Stripe Dashboard or API\n\nSince version `0.1.7` the extension also syncs subscriptions that were not created via Stripe Checkout, e.g. via the [Stripe Dashboard](https://support.stripe.com/questions/create-update-and-schedule-subscriptions) or [via Elements and the API](https://stripe.com/docs/billing/subscriptions/fixed-price).\n\nIn order for this to work, Firebase Authentication users need to be synced with Stripe customer objects and the customers collection in Cloud Firestore (new configuration added in version `0.1.7`).\n\n#### Get the customer&s;s subscription\n\nSubscription details are synced to the `subscriptions` sub-collection in the user&s;s corresponding customer doc.\n\n```js\ndb.collection(&s;${param:CUSTOMERS_COLLECTION}&s;)\n .doc(currentUser.uid)\n .collection(&s;subscriptions&s;)\n .where(&s;status&s;, &s;in&s;, [&s;trialing&s;, &s;active&s;])\n .onSnapshot(async (snapshot) =&g; {\n // In this implementation we only expect one active or trialing subscription to exist.\n const doc = snapshot.docs[0];\n console.log(doc.id, &s; =&g; &s;, doc.data());\n });\n```\n\n#### Redirect to the customer portal\n\nOnce a customer is subscribed you should show them a button to access the customer portal to view their invoices and manage their payment &a; subscription details. When the user clicks that button, call the `createPortalLink` function to get a portal link for them, then redirect them.\n\n```js\nconst functionRef = firebase\n .app()\n .functions(&s;${param:LOCATION}&s;)\n .httpsCallable(&s;${function:createPortalLink.name}&s;);\nconst { data } = await functionRef({\n returnUrl: window.location.origin,\n locale: \&q;auto\&q;, // Optional, defaults to \&q;auto\&q;\n configuration: \&q;bpc_1JSEAKHYgolSBA358VNoc2Hs\&q;, // Optional ID of a portal configuration: https://stripe.com/docs/api/customer_portal/configuration\n});\nwindow.location.assign(data.url);\n```\n\n#### Delete User Data\n\nYou have the option to automatically delete customer objects in Stripe by setting the deletion option in the configuration to \&q;Auto delete\&q;. In that case, when a user is deleted in Firebase Authentication, the extension will delete their customer object in Stripe which will immediately cancel all subscriptions for the user.\n\nThe extension will not delete any data from Cloud Firestore. Should you wish to delete the customer data from Cloud Firestore, you can use the [Delete User Data](https://firebase.google.com/products/extensions/delete-user-data) extension built by the Firebase team.\n\n### Monitoring\n\nAs a best practice, you can [monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor) of your installed extension, including checks on its health, usage, and logs.\n\nAccess the [Stripe dashboard](https://dashboard.stripe.com/) to manage all aspects of your Stripe account.\n\nEnjoy and please submit any feedback and feature requests on [GitHub](https://github.com/stripe/stripe-firebase-extensions/issues/new/choose)!\n&q;,&q;readmeContent&q;:&q;# Run Payments with Stripe\n\n**Author**: Stripe (**[https://stripe.com](https://stripe.com)**)\n\n**Description**: Controls access to paid content by syncing your one-time and recurring payments with Firebase Authentication.\n\n\n\n**Details**: Use this extension as a backend for your [Stripe](https://www.stripe.com/) payments.\n\nThe extension supports multiple use cases:\n\n- Process one-time payments with [Stripe Checkout](https://stripe.com/docs/payments/checkout) on the web.\n- Create subscriptions for your users and manage access control via Firebase Authentication.\n- Process payments &a; set up payment methods with the mobile payment sheet on [Android](https://stripe.com/docs/payments/accept-a-payment?platform=android&a;ui=payment-sheet), [iOS](https://stripe.com/docs/payments/accept-a-payment?platform=ios&a;ui=payment-sheet), or with [React Native](https://stripe.com/docs/payments/accept-a-payment?platform=react-native&a;ui=payment-sheet).\n\n#### Subscription payments with Stripe Checkout\n\nUsers can sign-up for your digital goods and paid content with Stripe Checkout and manage their subscriptions with the Stripe customer portal.\n\nThis extension syncs customers&s; subscription status with your Cloud Firestore and adds custom claims using Firebase Authentication for convenient access control in your application.\n\nThe design for Stripe Checkout and the customer portal can be customized in your Stripe Dashboard [branding settings](https://dashboard.stripe.com/settings/branding). See this example which is customized to match the Firebase color scheme:\n\n![Stripe Checkout Page](https://storage.googleapis.com/stripe-subscriptions-firebase-screenshots/firebase-stripe-subs-checkout.png)\n![Stripe Customer Portal](https://storage.googleapis.com/stripe-subscriptions-firebase-screenshots/firebase-stripe-subs-customer-portal.png)\n\n#### Recommended usage\n\nIf you&s;re building on the web platform, you can use this extension for any of your payment use cases. \n\nIf you&s;re developing native mobile applications and you&s;re selling digital products or services within your app, (e.g. subscriptions, in-game currencies, game levels, access to premium content, or unlocking a full version), you must use the app store&s;s in-app purchase APIs. See [Apple&s;s](https://developer.apple.com/app-store/review/guidelines/#payments) and [Google&s;s](https://support.google.com/googleplay/android-developer/answer/9858738?hl=en&a;ref_topic=9857752) guidelines for more information. \n\nFor all other scenarios you can use the [stripe-android](https://github.com/stripe/stripe-android), [stripe-ios](https://github.com/stripe/stripe-ios), [stripe-react-native](https://github.com/stripe/stripe-react-native), or [flutter_stripe](https://github.com/flutter-stripe/flutter_stripe) SDKs.\n\n#### Client SDK\n\nYou can use the [`@stripe/firestore-stripe-payments`](https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-web-sdk/README.md)\nJavaScript package to easily access this extension from web clients. This client SDK provides\nTypeScript type definitions and high-level convenience APIs for most common operations client\napplications would want to implement using the extension.\n\nUse a package manager like NPM to install the above package, and use it in conjunction with\nthe Firebase Web SDK.\n\n### Events\n\nThis extension emits events, which allows you to listen to and run custom logic at different trigger points during the functioning of the extension. For example you can listen to events when a product has been added via the `product.created` event, or whenever a payment has succeeded through the `invoice.payment_succeeded` event.\n\n#### Additional setup\n\nBefore installing this extension, set up the following Firebase services in your Firebase project:\n\n- [Cloud Firestore](https://firebase.google.com/docs/firestore) to store customer &a; subscription details.\n - Follow the steps in the [documentation](https://firebase.google.com/docs/firestore/quickstart#create) to create a Cloud Firestore database.\n- [Firebase Authentication](https://firebase.google.com/docs/auth) to enable different sign-up options for your users.\n - Enable the sign-in methods in the [Firebase console](https://console.firebase.google.com/project/_/authentication/providers) that you want to offer your users.\n\nThen, in the [Stripe Dashboard](https://dashboard.stripe.com):\n\n- Create a new [restricted key](https://stripe.com/docs/keys#limit-access) with write access for the \&q;Customers\&q;, \&q;Checkout Sessions\&q; and \&q;Customer portal\&q; resources, and read-only access for the \&q;Subscriptions\&q; and \&q;Prices\&q; resources.\n\n#### Billing\n\nThis extension uses the following Firebase services which may have associated charges:\n\n- Cloud Firestore\n- Cloud Functions\n- Cloud Secret Manager\n- Firebase Authentication\n- If you enable events [Eventarc fees apply](https://cloud.google.com/eventarc/pricing).\n\nThis extension also uses the following third-party services:\n\n- Stripe Payments ([pricing information](https://stripe.com/pricing))\n- Stripe Billing (when using subscriptions. [pricing information](https://stripe.com/pricing#billing-pricing))\n\nYou are responsible for any costs associated with your use of these services.\n\n#### Note from Firebase\n\nTo install this extension, your Firebase project must be on the Blaze (pay-as-you-go) plan. You will only be charged for the resources you use. Most Firebase services offer a free tier for low-volume use. [Learn more about Firebase billing.](https://firebase.google.com/pricing)\n\nStarting August 17 2020, you will be billed a small amount (typically less than $0.10) when you install or reconfigure this extension. See the [Cloud Functions for Firebase billing FAQ](https://firebase.google.com/support/faq#expandable-15) for a detailed explanation.\n\n\n\n**Configuration Parameters:**\n\n* Cloud Functions deployment location: Where do you want to deploy the functions created for this extension? You usually want a location close to your database. For help selecting a location, refer to the [location selection guide](https://firebase.google.com/docs/functions/locations).\n\n* Products and pricing plans collection: What is the path to the Cloud Firestore collection where the extension should store Stripe pricing plans?\n\n* Customer details and subscriptions collection: What is the path to the Cloud Firestore collection where the extension should store Stripe customer details? This can be the location of an existing user collection, the extension will not overwrite your existing data but rather merge the Stripe data into your existing `uid` docs.\n\n* Stripe configuration collection: What is the path to the Cloud Firestore collection where the extension should store Stripe configuration?\n\n* Sync new users to Stripe customers and Cloud Firestore: Do you want to automatically sync new users to customer objects in Stripe? If set to &s;Sync&s;, the extension will create a new customer object in Stripe and add a new doc to the customer collection in Firestore when a new user signs up via Firebase Authentication. If set to &s;Do not sync&s; (default), the extension will create the customer object \&q;on the fly\&q; with the first checkout session creation.\n\n* Automatically delete Stripe customer objects: Do you want to automatically delete customer objects in Stripe? When a user is deleted in Firebase Authentication or in Cloud Firestore and set to &s;Auto delete&s; the extension will delete their customer object in Stripe which will immediately cancel all subscriptions for the user.\n\n* Stripe API key with restricted access: What is your Stripe API key? We recommend creating a new [restricted key](https://stripe.com/docs/keys#limit-access) with write access only for the \&q;Customers\&q;, \&q;Checkout Sessions\&q; and \&q;Customer portal\&q; resources. And read-only access for the \&q;Subscriptions\&q; and \&q;Prices\&q; resources.\n\n* Stripe webhook secret: This is your signing secret for a Stripe-registered webhook. This webhook can only be registered after installation. Leave this value untouched during installation, then follow the postinstall instructions for registering your webhook and configuring this value.\n\n* Minimum instances for createCheckoutSession function: Set the minimum number of function instances that should be always be available to create Checkout Sessions. This number can be adjusted to reduce cold starts and increase the responsiveness of Checkout Session creation requests. Suggested values are 0 or 1. Please note this setting will likely incur billing costss, see the [Firebase documentation](https://firebase.google.com/docs/functions/manage-functions#reduce_the_number_of_cold_starts) for more information.\n\n\n\n**Cloud Functions:**\n\n* **createCustomer:** Creates a Stripe customer object when a new user signs up.\n\n* **createCheckoutSession:** Creates a Checkout session to collect the customer&s;s payment details.\n\n* **createPortalLink:** Creates links to the customer portal for the user to manage their payment &a; subscription details.\n\n* **handleWebhookEvents:** Handles Stripe webhook events to keep subscription statuses in sync and update custom claims.\n\n* **onUserDeleted:** Deletes the Stripe customer object and cancels all their subscriptions when the user is deleted in Firebase Authentication.\n\n* **onCustomerDataDeleted:** Deletes the Stripe customer object and cancels all their subscriptions when the customer doc in Cloud Firestore is deleted.\n\n\n\n**Access Required**:\n\n\n\nThis extension will operate with the following project IAM roles:\n\n* firebaseauth.admin (Reason: Allows the extension to set custom claims for users.)\n\n* datastore.user (Reason: Allows the extension to store customers &a; subscriptions in Cloud Firestore.)\n&q;,&q;externalServices&q;:[{&q;name&q;:&q;Stripe&q;,&q;pricingUri&q;:&q;https://stripe.com/pricing&q;}],&q;displayName&q;:&q;Run Payments with Stripe&q;,&q;events&q;:[{&q;type&q;:&q;com.stripe.v1.product.created&q;,&q;description&q;:&q;Occurs whenever a product is created.&q;},{&q;type&q;:&q;com.stripe.v1.product.updated&q;,&q;description&q;:&q;Occurs whenever a product is updated.&q;},{&q;type&q;:&q;com.stripe.v1.product.deleted&q;,&q;description&q;:&q;Occurs whenever a product is deleted.&q;},{&q;type&q;:&q;com.stripe.v1.price.created&q;,&q;description&q;:&q;Occurs whenever a price is created.&q;},{&q;type&q;:&q;com.stripe.v1.price.updated&q;,&q;description&q;:&q;Occurs whenever a price is updated.&q;},{&q;type&q;:&q;com.stripe.v1.price.deleted&q;,&q;description&q;:&q;Occurs whenever a price is deleted.&q;},{&q;type&q;:&q;com.stripe.v1.checkout.session.completed&q;,&q;description&q;:&q;Occurs when a Checkout Session has been successfully completed.&q;},{&q;type&q;:&q;com.stripe.v1.checkout.session.async_payment_succeeded&q;,&q;description&q;:&q;Occurs when a payment intent using a delayed payment method finally succeeds.&q;},{&q;type&q;:&q;com.stripe.v1.checkout.session.async_payment_failed&q;,&q;description&q;:&q;Occurs when a payment intent using a delayed payment method fails.&q;},{&q;type&q;:&q;com.stripe.v1.customer.subscription.created&q;,&q;description&q;:&q;Occurs whenever a customer is signed up for a new plan.&q;},{&q;type&q;:&q;com.stripe.v1.customer.subscription.updated&q;,&q;description&q;:&q;Occurs whenever a subscription changes (e.g., switching from one plan to another, or changing the status from trial to active).&q;},{&q;type&q;:&q;com.stripe.v1.customer.subscription.deleted&q;,&q;description&q;:&q;Occurs whenever a customer&s;s subscription ends.&q;},{&q;type&q;:&q;com.stripe.v1.tax_rate.created&q;,&q;description&q;:&q;Occurs whenever a new tax rate is created.&q;},{&q;type&q;:&q;com.stripe.v1.tax_rate.updated&q;,&q;description&q;:&q;Occurs whenever a tax rate is updated.&q;},{&q;type&q;:&q;com.stripe.v1.invoice.paid&q;,&q;description&q;:&q;Occurs whenever an invoice payment attempt succeeds or an invoice is marked as paid out-of-band.&q;},{&q;type&q;:&q;com.stripe.v1.invoice.payment_succeeded&q;,&q;description&q;:&q;Occurs whenever an invoice payment attempt succeeds.&q;},{&q;type&q;:&q;com.stripe.v1.invoice.payment_failed&q;,&q;description&q;:&q;Occurs whenever an invoice payment attempt fails, due either to a declined payment or to the lack of a stored payment method.&q;},{&q;type&q;:&q;com.stripe.v1.invoice.upcoming&q;,&q;description&q;:&q;Occurs X number of days before a subscription is scheduled to create an invoice that is automatically charged—where X is determined by your subscriptions settings.&q;},{&q;type&q;:&q;com.stripe.v1.invoice.marked_uncollectible&q;,&q;description&q;:&q;Occurs whenever an invoice is marked uncollectible.&q;},{&q;type&q;:&q;com.stripe.v1.invoice.payment_action_required&q;,&q;description&q;:&q;Occurs whenever an invoice payment attempt requires further user action to complete.&q;},{&q;type&q;:&q;com.stripe.v1.payment_intent.processing&q;,&q;description&q;:&q;Occurs when a PaymentIntent has started processing.&q;},{&q;type&q;:&q;com.stripe.v1.payment_intent.succeeded&q;,&q;description&q;:&q;Occurs when a PaymentIntent has successfully completed payment.&q;},{&q;type&q;:&q;com.stripe.v1.payment_intent.canceled&q;,&q;description&q;:&q;Occurs when a PaymentIntent is canceled.&q;},{&q;type&q;:&q;com.stripe.v1.payment_intent.payment_failed&q;,&q;description&q;:&q;Occurs when a PaymentIntent has failed the attempt to create a payment method or a payment.&q;}],&q;systemParams&q;:[{&q;param&q;:&q;firebaseextensions.v1beta.function/timeoutSeconds&q;,&q;label&q;:&q;Function timeout seconds&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;How long should functions run before timing out, in seconds (0-540)?&q;,&q;validationRegex&q;:&q;^[1-9][0-9]{0,2}$&q;,&q;validationErrorMessage&q;:&q;Function timeout should be an integer number of seconds, between 0 and 540&q;,&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/vpcConnector&q;,&q;label&q;:&q;VPC Connector&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;The VPC Network Connector that this cloud function can connect to. It can be either the fully-qualified URI, or the short name of the network connector resource. The format of this field is projects/*/locations/*/connectors/*.&q;,&q;validationRegex&q;:&q;^projects/([^/]+)/locations/([^/]+)/connectors/([^/]+)$&q;,&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/vpcConnectorEgressSettings&q;,&q;label&q;:&q;VPC Connector Egress settings&q;,&q;type&q;:&q;SELECT&q;,&q;description&q;:&q;Controls outgoing traffic when a VPC connector is configured&q;,&q;options&q;:[{&q;value&q;:&q;VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED&q;,&q;label&q;:&q;Unspecified&q;},{&q;value&q;:&q;PRIVATE_RANGES_ONLY&q;,&q;label&q;:&q;Private ranges only&q;},{&q;value&q;:&q;ALL_TRAFFIC&q;,&q;label&q;:&q;All traffic&q;}],&q;default&q;:&q;VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED&q;,&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/minInstances&q;,&q;label&q;:&q;Minimum function instances&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;The minimum number of instances of each function to run at once (0-1000)&q;,&q;default&q;:&q;0&q;,&q;validationRegex&q;:&q;^[0-9]*$&q;,&q;validationErrorMessage&q;:&q;Min instances must be a non-negative integer.&q;,&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/maxInstances&q;,&q;label&q;:&q;Maximum function instances&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;The maximum number of instances of each function to run at once&q;,&q;validationRegex&q;:&q;^[0-9]*$&q;,&q;validationErrorMessage&q;:&q;Max instances must be a non-negative integer.&q;,&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/ingressSettings&q;,&q;label&q;:&q;Function ingress settings&q;,&q;type&q;:&q;SELECT&q;,&q;description&q;:&q;Where should functions allow incoming traffic from?&q;,&q;options&q;:[{&q;value&q;:&q;ALLOW_ALL&q;,&q;label&q;:&q;Allow all&q;},{&q;value&q;:&q;ALLOW_INTERNAL_ONLY&q;,&q;label&q;:&q;Allow internal only&q;},{&q;value&q;:&q;ALLOW_INTERNAL_AND_GCLB&q;,&q;label&q;:&q;Allow internal and GCLB&q;}],&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/labels&q;,&q;label&q;:&q;Function labels&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;Do you wish to set any labels on this instance&s;s Cloud Functions? If so, provide up to 55 labels in the format &s;key1:value, key2:value&s;&q;,&q;validationRegex&q;:&q;^([a-zžà-ÿ][A-Za-zŽžÀ-ÿ0-9_-]{0,62}:[A-Za-zŽžÀ-ÿ0-9_-]{0,63},\\s*)*([a-zžà-ÿ][A-Za-zŽžÀ-ÿ0-9_-]{0,62}:[A-Za-zŽžÀ-ÿ0-9_-]{0,63})$&q;,&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/kmsKeyName&q;,&q;label&q;:&q;KMS key name&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;Do you want to use a Customer Managed Encryption Key (CMEK) to encrypt this extension&s;s functions? If you set this, you must also set a Docker repository encrypted by that key. See https://cloud.google.com/functions/docs/securing/cmek for more details.&q;,&q;validationRegex&q;:&q;^projects/([^/]+)/locations/([^/]+)/keyRings/([^/]+)/cryptoKeys/([^/]+)$&q;,&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/dockerRepository&q;,&q;label&q;:&q;Docker repository&q;,&q;type&q;:&q;STRING&q;,&q;description&q;:&q;What Docker repository should be used to store function images? Default repository will be used if not set.&q;,&q;validationRegex&q;:&q;^projects/([^/]+)/locations/([^/]+)/repositories/([^/]+)$&q;,&q;advanced&q;:true},{&q;param&q;:&q;firebaseextensions.v1beta.function/memory&q;,&q;label&q;:&q;Function memory&q;,&q;type&q;:&q;SELECT&q;,&q;description&q;:&q;How much memory should be allocated to each v1 function?&q;,&q;options&q;:[{&q;value&q;:&q;128&q;,&q;label&q;:&q;128MB&q;},{&q;value&q;:&q;256&q;,&q;label&q;:&q;256MB&q;},{&q;value&q;:&q;512&q;,&q;label&q;:&q;512MB&q;},{&q;value&q;:&q;1024&q;,&q;label&q;:&q;1GB&q;},{&q;value&q;:&q;2048&q;,&q;label&q;:&q;2GB&q;},{&q;value&q;:&q;4096&q;,&q;label&q;:&q;4GB&q;},{&q;value&q;:&q;8192&q;,&q;label&q;:&q;8GB&q;}],&q;default&q;:&q;256&q;,&q;advanced&q;:true}]},&q;state&q;:&q;DEPRECATED&q;,&q;hash&q;:&q;d8ab13445f1cf92b77a4067820bf0fd9b57bc1d1eaa30fa1eb080add3680c5b6&q;,&q;createTime&q;:&q;2023-08-14T09:58:54.399327Z&q;,&q;sourceDownloadUri&q;:&q;https://storage.googleapis.com/firebase-mod-sources-prod/d8ab13445f1cf92b77a4067820bf0fd9b57bc1d1eaa30fa1eb080add3680c5b6&q;,&q;id&q;:&q;0.3.4&q;,&q;releaseNotes&q;:&q;This extension has been formally transferred to Invertase. See the updated README for more details.\n&q;,&q;buildSourceUri&q;:&q;https://github.com/stripe/stripe-firebase-extensions/tree/107031923116d776ace0d33011a28d29e48fe827&q;,&q;listing&q;:{&q;state&q;:&q;APPROVED&q;},&q;metrics&q;:{&q;activeInstallCount&q;:&q;5100&q;},&q;extensionRoot&q;:&q;firestore-stripe-payments&q;,&q;deprecationMessage&q;:&q;\n This extension has been officially transferred by Stripe to \n &l;a \n class=\&q;text-primary underline inline-flex gap-0.5\&q;\n href=&s;https://extensions.dev/extensions/invertase/firestore-stripe-payments&s;\n &g;\n Invertase\n &l;/a&g;, who will maintain the extension going forward. Please see \n &l;a\n class=\&q;text-primary underline inline-flex gap-0.5\&q;\n href=&s;https://github.com/invertase/stripe-firebase-extensions/issues/524&s;\n &g;this issue&l;/a&g;\n for more details. \n It is now recommended to uninstall this extension and install\n &l;a \n class=\&q;text-primary underline inline-flex gap-0.5\&q;\n href=&s;https://extensions.dev/extensions/invertase/firestore-stripe-payments&s;\n &g;\n invertase/firestore-stripe-payments\n &l;/a&g;\n instead.\n &q;},&q;additional-content-stripe/firestore-stripe-payments&q;:[]}</script></body><!-- This page was prerendered with Angular Universal --></html>

Pages: 1 2 3 4 5 6 7 8 9 10