Initial Commit

This commit is contained in:
David Stone
2024-11-30 18:24:12 -07:00
commit e8f7955c1c
5432 changed files with 1397750 additions and 0 deletions
LICENSE
assets
css
fonts
img
badge.pngbg-setup.png
checkout-forms
empty-state-bg.pngerasmo-carlos.jpg
flags
1x1
ad.svgae.svgaf.svgag.svgai.svgal.svgam.svgao.svgaq.svgar.svgas.svgat.svgau.svgaw.svgax.svgaz.svgba.svgbb.svgbd.svgbe.svgbf.svgbg.svgbh.svgbi.svgbj.svgbl.svgbm.svgbn.svgbo.svgbq.svgbr.svgbs.svgbt.svgbv.svgbw.svgby.svgbz.svgca.svgcc.svgcd.svgcf.svgcg.svgch.svgci.svgck.svgcl.svgcm.svgcn.svgco.svgcr.svgcu.svgcv.svgcw.svgcx.svgcy.svgcz.svgde.svgdj.svgdk.svgdm.svgdo.svgdz.svgec.svgee.svgeg.svgeh.svger.svges-ca.svges-ga.svges.svget.svgeu.svgfi.svgfj.svgfk.svgfm.svgfo.svgfr.svgga.svggb-eng.svggb-nir.svggb-sct.svggb-wls.svggb.svggd.svgge.svggf.svggg.svggh.svggi.svggl.svggm.svggn.svggp.svggq.svggr.svggs.svggt.svggu.svggw.svggy.svghk.svghm.svghn.svghr.svght.svghu.svgid.svgie.svgil.svgim.svgin.svgio.svgiq.svgir.svgis.svgit.svgje.svgjm.svgjo.svgjp.svgke.svgkg.svgkh.svgki.svgkm.svgkn.svgkp.svgkr.svgkw.svgky.svgkz.svgla.svglb.svglc.svgli.svglk.svglr.svgls.svglt.svglu.svglv.svgly.svgma.svgmc.svgmd.svgme.svgmf.svgmg.svgmh.svgmk.svgml.svgmm.svgmn.svgmo.svgmp.svgmq.svgmr.svgms.svgmt.svgmu.svgmv.svgmw.svgmx.svgmy.svgmz.svgna.svgnc.svgne.svgnf.svgng.svgni.svgnl.svgno.svgnp.svgnr.svgnu.svgnz.svgom.svgpa.svgpe.svgpf.svgpg.svgph.svgpk.svgpl.svgpm.svgpn.svgpr.svgps.svgpt.svgpw.svgpy.svgqa.svgre.svgro.svgrs.svgru.svgrw.svgsa.svgsb.svgsc.svgsd.svgse.svgsg.svgsh.svgsi.svgsj.svgsk.svgsl.svgsm.svgsn.svgso.svgsr.svgss.svgst.svgsv.svgsx.svgsy.svgsz.svgtc.svgtd.svgtf.svgtg.svgth.svgtj.svgtk.svgtl.svgtm.svgtn.svgto.svgtr.svgtt.svgtv.svgtw.svgtz.svgua.svgug.svgum.svgun.svgus.svguy.svguz.svgva.svgvc.svgve.svgvg.svgvi.svgvn.svgvu.svgwf.svgws.svgxk.svgye.svgyt.svgza.svgzm.svgzw.svg
4x3
ad.svgae.svgaf.svgag.svgai.svgal.svgam.svgao.svgaq.svgar.svgas.svgat.svgau.svgaw.svgax.svgaz.svgba.svgbb.svgbd.svgbe.svgbf.svgbg.svgbh.svgbi.svgbj.svgbl.svgbm.svgbn.svgbo.svgbq.svgbr.svgbs.svgbt.svgbv.svgbw.svgby.svgbz.svgca.svgcc.svgcd.svgcf.svgcg.svgch.svgci.svgck.svgcl.svgcm.svgcn.svgco.svgcr.svgcu.svgcv.svgcw.svgcx.svgcy.svgcz.svgde.svgdj.svgdk.svgdm.svgdo.svgdz.svgec.svgee.svgeg.svgeh.svger.svges-ca.svges-ga.svges.svget.svgeu.svgfi.svgfj.svgfk.svgfm.svgfo.svgfr.svgga.svggb-eng.svggb-nir.svggb-sct.svggb-wls.svggb.svggd.svgge.svggf.svggg.svggh.svggi.svggl.svggm.svggn.svggp.svggq.svggr.svggs.svggt.svggu.svggw.svggy.svghk.svghm.svghn.svghr.svght.svghu.svgid.svgie.svgil.svgim.svgin.svgio.svgiq.svgir.svgis.svgit.svgje.svgjm.svgjo.svgjp.svgke.svgkg.svgkh.svgki.svgkm.svgkn.svgkp.svgkr.svgkw.svgky.svgkz.svgla.svglb.svglc.svgli.svglk.svglr.svgls.svglt.svglu.svglv.svgly.svgma.svgmc.svgmd.svgme.svgmf.svgmg.svgmh.svgmk.svgml.svgmm.svgmn.svgmo.svgmp.svgmq.svgmr.svgms.svgmt.svgmu.svgmv.svgmw.svgmx.svgmy.svgmz.svgna.svgnc.svgne.svgnf.svgng.svgni.svgnl.svgno.svgnp.svgnr.svgnu.svgnz.svgom.svgpa.svgpe.svgpf.svgpg.svgph.svgpk.svgpl.svgpm.svgpn.svgpr.svgps.svgpt.svgpw.svgpy.svgqa.svgre.svgro.svgrs.svgru.svgrw.svgsa.svgsb.svgsc.svgsd.svgse.svgsg.svgsh.svgsi.svgsj.svgsk.svgsl.svgsm.svgsn.svgso.svgsr.svgss.svgst.svgsv.svgsx.svgsy.svgsz.svgtc.svgtd.svgtf.svgtg.svgth.svgtj.svgtk.svgtl.svgtm.svgtn.svgto.svgtr.svgtt.svgtv.svgtw.svgtz.svgua.svgug.svgum.svgun.svgus.svguy.svguz.svgva.svgvc.svgve.svgvg.svgvi.svgvn.svgvu.svgwf.svgws.svgxk.svgye.svgyt.svgza.svgzm.svgzw.svg
hosts
loader.svglogo.pngno-preview.pngpattern-wp-ultimo.png
settings
sidebar
site-placeholder-image.png
wizards
wp-ultimo-screenshot.png
js
addons.jsaddons.min.jsadmin-notices.jsadmin-notices.min.jsadmin-screen.jsadmin-screen.min.jsadmin.jsadmin.min.jsapp.jsapp.min.jscheckout-form-editor-modal.jscheckout-form-editor-modal.min.jscheckout-forms-editor.jscheckout-forms-editor.min.jscheckout.jscheckout.min.jscookie-helpers.jscookie-helpers.min.jscustomizer.jscustomizer.min.jsdashboard-statistics.jsdashboard-statistics.min.jsedit-placeholders.jsedit-placeholders.min.jsemail-edit-page.jsemail-edit-page.min.jsevent-view-page.jsevent-view-page.min.jsfields.jsfields.min.jsfunctions.jsfunctions.min.js
gateways
gutenberg-support.jsgutenberg-support.min.jsjumper.jsjumper.min.jslegacy-signup.jslegacy-signup.min.js
lib
list-tables.jslist-tables.min.jsscreenshot-scraper.jsscreenshot-scraper.min.jsselectizer.jsselectizer.min.jssetup-wizard-polyfill.jssetup-wizard-polyfill.min.jssetup-wizard.jssetup-wizard.min.jssite-maintenance.jssite-maintenance.min.jssso.jssso.min.jssupport.jssupport.min.jstax-rates.jstax-rates.min.jstax-statistics.jstax-statistics.min.jstemplate-previewer.jstemplate-previewer.min.jstemplate-switching.jstemplate-switching.min.jsthank-you.jsthank-you.min.jstours.jstours.min.jsurl-preview.jsurl-preview.min.jsview-logs.jsview-logs.min.jsvisits-counter.jsvisits-counter.min.jsvue-apps.jsvue-apps.min.jswebhook-list-page.jswebhook-list-page.min.jswebhook-page.jswebhook-page.min.jswubox.jswubox.min.js
autoload.phpconstants.php
data
dependencies
amphp
amp
byte-stream
cache
dns
hpack
http-client
http
parser
process
serialization
socket
sync
windows-registry
autoload.php
berlindb
composer
daverandom
delight-im
doctrine
deprecations
lib
Doctrine
guzzlehttp
guzzle
promises
psr7
hashids
ifsnop
mysqldump-php
src
Ifsnop
Mysqldump
jasny
kelunik
league
mexitek
phpcolors
demo
src
Mexitek
PHPColors
mpdf
mpdf
CREDITS.txt
data
CJKdata.php
collations
Afrikaans_South_Africa.phpAlbanian_Albania.phpAlsatian_France.phpArabic_Algeria.phpArabic_Bahrain.phpArabic_Egypt.phpArabic_Iraq.phpArabic_Jordan.phpArabic_Kuwait.phpArabic_Lebanon.phpArabic_Libya.phpArabic_Morocco.phpArabic_Oman.phpArabic_Pseudo_RTL.phpArabic_Qatar.phpArabic_Saudi_Arabia.phpArabic_Syria.phpArabic_Tunisia.phpArabic_Yemen.phpAzeri_(Cyrillic)_Azerbaijan.phpAzeri_(Latin)_Azerbaijan.phpBashkir_Russia.phpBasque_Spain.phpBelarusian_Belarus.phpBosnian_(Cyrillic)_Bosnia_and_Herzegovina.phpBosnian_(Latin)_Bosnia_and_Herzegovina.phpBreton_France.phpBulgarian_Bulgaria.phpCatalan_Spain.phpCorsican_France.phpCroatian_(Latin)_Bosnia_and_Herzegovina.phpCroatian_Croatia.phpCzech_Czech_Republic.phpDanish_Denmark.phpDari_Afghanistan.phpDutch_Belgium.phpDutch_Netherlands.phpEnglish_Australia.phpEnglish_Belize.phpEnglish_Canada.phpEnglish_Caribbean.phpEnglish_India.phpEnglish_Ireland.phpEnglish_Jamaica.phpEnglish_Malaysia.phpEnglish_New_Zealand.phpEnglish_Republic_of_the_Philippines.phpEnglish_Singapore.phpEnglish_South_Africa.phpEnglish_Trinidad_and_Tobago.phpEnglish_United_Kingdom.phpEnglish_United_States.phpEnglish_Zimbabwe.phpEstonian_Estonia.phpFaroese_Faroe_Islands.phpFilipino_Philippines.phpFinnish_Finland.phpFrench_Belgium.phpFrench_Canada.phpFrench_France.phpFrench_Luxembourg.phpFrench_Principality_of_Monaco.phpFrench_Switzerland.phpFrisian_Netherlands.phpGalician_Spain.phpGerman_Austria.phpGerman_Germany.phpGerman_Liechtenstein.phpGerman_Luxembourg.phpGerman_Switzerland.phpGreek_Greece.phpGreenlandic_Greenland.phpHausa_(Latin)_Nigeria.phpHebrew_Israel.phpHungarian_Hungary.phpIcelandic_Iceland.phpIgbo_Nigeria.phpIndonesian_Indonesia.phpInuktitut_(Latin)_Canada.phpInvariant_Language_Invariant_Country.phpIrish_Ireland.phpItalian_Italy.phpItalian_Switzerland.phpKinyarwanda_Rwanda.phpKiswahili_Kenya.phpKyrgyz_Kyrgyzstan.phpLatvian_Latvia.phpLithuanian_Lithuania.phpLower_Sorbian_Germany.phpLuxembourgish_Luxembourg.phpMacedonian_(FYROM)_Macedonia_(FYROM).phpMalay_Brunei_Darussalam.phpMalay_Malaysia.phpMapudungun_Chile.phpMohawk_Canada.phpMongolian_(Cyrillic)_Mongolia.phpNorwegian_(Nynorsk)_Norway.phpOccitan_France.phpPersian_Iran.phpPolish_Poland.phpPortuguese_Brazil.phpPortuguese_Portugal.phpQuechua_Bolivia.phpQuechua_Ecuador.phpQuechua_Peru.phpRomanian_Romania.phpRomansh_Switzerland.phpRussian_Russia.phpSami_(Inari)_Finland.phpSami_(Lule)_Norway.phpSami_(Lule)_Sweden.phpSami_(Northern)_Finland.phpSami_(Northern)_Norway.phpSami_(Northern)_Sweden.phpSami_(Skolt)_Finland.phpSami_(Southern)_Norway.phpSami_(Southern)_Sweden.phpSerbian_(Cyrillic)_Bosnia_and_Herzegovina.phpSerbian_(Cyrillic)_Serbia.phpSerbian_(Latin)_Bosnia_and_Herzegovina.phpSerbian_(Latin)_Serbia.phpSesotho_sa_Leboa_South_Africa.phpSetswana_South_Africa.phpSlovak_Slovakia.phpSlovenian_Slovenia.phpSpanish_Argentina.phpSpanish_Bolivia.phpSpanish_Chile.phpSpanish_Colombia.phpSpanish_Costa_Rica.phpSpanish_Dominican_Republic.phpSpanish_Ecuador.phpSpanish_El_Salvador.phpSpanish_Guatemala.phpSpanish_Honduras.phpSpanish_Mexico.phpSpanish_Nicaragua.phpSpanish_Panama.phpSpanish_Paraguay.phpSpanish_Peru.phpSpanish_Puerto_Rico.phpSpanish_Spain.phpSpanish_United_States.phpSpanish_Uruguay.phpSpanish_Venezuela.phpSwedish_Finland.phpSwedish_Sweden.phpTajik_(Cyrillic)_Tajikistan.phpTamazight_(Latin)_Algeria.phpTatar_Russia.phpTurkish_Turkey.phpTurkmen_Turkmenistan.phpUkrainian_Ukraine.phpUpper_Sorbian_Germany.phpUrdu_Islamic_Republic_of_Pakistan.phpUzbek_(Cyrillic)_Uzbekistan.phpUzbek_(Latin)_Uzbekistan.phpVietnamese_Vietnam.phpWelsh_United_Kingdom.phpWolof_Senegal.phpYakut_Russia.phpYoruba_Nigeria.phpisiXhosa_South_Africa.phpisiZulu_South_Africa.php
entity_substitutions.php
font
iccprofiles
lang2fonts.csslinebrdictK.datlinebrdictL.datlinebrdictT.datmpdf.cssno_image.jpgout.php
patterns
subs_core.phpsubs_win-1252.phpupperCase.php
phpunit.xmlruleset.xml
src
AssetFetcher.phpBarcode.php
Barcode
Cache.php
Color
Config
Container
Conversion
Css
CssManager.phpDirectWrite.php
Exception
File
Fonts
Form.phpFpdiTrait.php
Gif
Gradient.phpHTMLParserMode.php
Http
Hyphenator.php
Image
Language
Log
Mpdf.phpMpdfException.phpMpdfImageException.phpOtl.phpOtlDump.php
Output
PageBox.phpPageFormat.php
Pdf
ServiceFactory.php
Shaper
SizeConverter.phpStrict.phpTTFontFile.phpTTFontFileAnalysis.phpTableOfContents.phpTag.php
Tag
Ucdn.php
Utils
Watermark.phpWatermarkImage.phpWatermarkText.php
Writer
functions-dev.phpfunctions.php
ttfonts
psr-http-message-shim
psr-log-aware-trait
myclabs
nesbot
carbon
extension.neon
lazy
sponsors.php
src
Carbon
AbstractTranslator.phpCarbon.phpCarbonConverterInterface.phpCarbonImmutable.phpCarbonInterface.phpCarbonInterval.phpCarbonPeriod.phpCarbonPeriodImmutable.phpCarbonTimeZone.php
Cli
Doctrine
Exceptions
Factory.phpFactoryImmutable.php
Lang
aa.phpaa_DJ.phpaa_ER.phpaa_ER@saaho.phpaa_ET.phpaf.phpaf_NA.phpaf_ZA.phpagq.phpagr.phpagr_PE.phpak.phpak_GH.phpam.phpam_ET.phpan.phpan_ES.phpanp.phpanp_IN.phpar.phpar_AE.phpar_BH.phpar_DJ.phpar_DZ.phpar_EG.phpar_EH.phpar_ER.phpar_IL.phpar_IN.phpar_IQ.phpar_JO.phpar_KM.phpar_KW.phpar_LB.phpar_LY.phpar_MA.phpar_MR.phpar_OM.phpar_PS.phpar_QA.phpar_SA.phpar_SD.phpar_SO.phpar_SS.phpar_SY.phpar_Shakl.phpar_TD.phpar_TN.phpar_YE.phpas.phpas_IN.phpasa.phpast.phpast_ES.phpayc.phpayc_PE.phpaz.phpaz_AZ.phpaz_Cyrl.phpaz_IR.phpaz_Latn.phpbas.phpbe.phpbe_BY.phpbe_BY@latin.phpbem.phpbem_ZM.phpber.phpber_DZ.phpber_MA.phpbez.phpbg.phpbg_BG.phpbhb.phpbhb_IN.phpbho.phpbho_IN.phpbi.phpbi_VU.phpbm.phpbn.phpbn_BD.phpbn_IN.phpbo.phpbo_CN.phpbo_IN.phpbr.phpbr_FR.phpbrx.phpbrx_IN.phpbs.phpbs_BA.phpbs_Cyrl.phpbs_Latn.phpbyn.phpbyn_ER.phpca.phpca_AD.phpca_ES.phpca_ES_Valencia.phpca_FR.phpca_IT.phpccp.phpccp_IN.phpce.phpce_RU.phpcgg.phpchr.phpchr_US.phpckb.phpcmn.phpcmn_TW.phpcrh.phpcrh_UA.phpcs.phpcs_CZ.phpcsb.phpcsb_PL.phpcu.phpcv.phpcv_RU.phpcy.phpcy_GB.phpda.phpda_DK.phpda_GL.phpdav.phpde.phpde_AT.phpde_BE.phpde_CH.phpde_DE.phpde_IT.phpde_LI.phpde_LU.phpdje.phpdoi.phpdoi_IN.phpdsb.phpdsb_DE.phpdua.phpdv.phpdv_MV.phpdyo.phpdz.phpdz_BT.phpebu.phpee.phpee_TG.phpel.phpel_CY.phpel_GR.phpen.phpen_001.phpen_150.phpen_AG.phpen_AI.phpen_AS.phpen_AT.phpen_AU.phpen_BB.phpen_BE.phpen_BI.phpen_BM.phpen_BS.phpen_BW.phpen_BZ.phpen_CA.phpen_CC.phpen_CH.phpen_CK.phpen_CM.phpen_CX.phpen_CY.phpen_DE.phpen_DG.phpen_DK.phpen_DM.phpen_ER.phpen_FI.phpen_FJ.phpen_FK.phpen_FM.phpen_GB.phpen_GD.phpen_GG.phpen_GH.phpen_GI.phpen_GM.phpen_GU.phpen_GY.phpen_HK.phpen_IE.phpen_IL.phpen_IM.phpen_IN.phpen_IO.phpen_ISO.phpen_JE.phpen_JM.phpen_KE.phpen_KI.phpen_KN.phpen_KY.phpen_LC.phpen_LR.phpen_LS.phpen_MG.phpen_MH.phpen_MO.phpen_MP.phpen_MS.phpen_MT.phpen_MU.phpen_MW.phpen_MY.phpen_NA.phpen_NF.phpen_NG.phpen_NL.phpen_NR.phpen_NU.phpen_NZ.phpen_PG.phpen_PH.phpen_PK.phpen_PN.phpen_PR.phpen_PW.phpen_RW.phpen_SB.phpen_SC.phpen_SD.phpen_SE.phpen_SG.phpen_SH.phpen_SI.phpen_SL.phpen_SS.phpen_SX.phpen_SZ.phpen_TC.phpen_TK.phpen_TO.phpen_TT.phpen_TV.phpen_TZ.phpen_UG.phpen_UM.phpen_US.phpen_US_Posix.phpen_VC.phpen_VG.phpen_VI.phpen_VU.phpen_WS.phpen_ZA.phpen_ZM.phpen_ZW.phpeo.phpes.phpes_419.phpes_AR.phpes_BO.phpes_BR.phpes_BZ.phpes_CL.phpes_CO.phpes_CR.phpes_CU.phpes_DO.phpes_EA.phpes_EC.phpes_ES.phpes_GQ.phpes_GT.phpes_HN.phpes_IC.phpes_MX.phpes_NI.phpes_PA.phpes_PE.phpes_PH.phpes_PR.phpes_PY.phpes_SV.phpes_US.phpes_UY.phpes_VE.phpet.phpet_EE.phpeu.phpeu_ES.phpewo.phpfa.phpfa_AF.phpfa_IR.phpff.phpff_CM.phpff_GN.phpff_MR.phpff_SN.phpfi.phpfi_FI.phpfil.phpfil_PH.phpfo.phpfo_DK.phpfo_FO.phpfr.phpfr_BE.phpfr_BF.phpfr_BI.phpfr_BJ.phpfr_BL.phpfr_CA.phpfr_CD.phpfr_CF.phpfr_CG.phpfr_CH.phpfr_CI.phpfr_CM.phpfr_DJ.phpfr_DZ.phpfr_FR.phpfr_GA.phpfr_GF.phpfr_GN.phpfr_GP.phpfr_GQ.phpfr_HT.phpfr_KM.phpfr_LU.phpfr_MA.phpfr_MC.phpfr_MF.phpfr_MG.phpfr_ML.phpfr_MQ.phpfr_MR.phpfr_MU.phpfr_NC.phpfr_NE.phpfr_PF.phpfr_PM.phpfr_RE.phpfr_RW.phpfr_SC.phpfr_SN.phpfr_SY.phpfr_TD.phpfr_TG.phpfr_TN.phpfr_VU.phpfr_WF.phpfr_YT.phpfur.phpfur_IT.phpfy.phpfy_DE.phpfy_NL.phpga.phpga_IE.phpgd.phpgd_GB.phpgez.phpgez_ER.phpgez_ET.phpgl.phpgl_ES.phpgom.phpgom_Latn.phpgsw.phpgsw_CH.phpgsw_FR.phpgsw_LI.phpgu.phpgu_IN.phpguz.phpgv.phpgv_GB.phpha.phpha_GH.phpha_NE.phpha_NG.phphak.phphak_TW.phphaw.phphe.phphe_IL.phphi.phphi_IN.phphif.phphif_FJ.phphne.phphne_IN.phphr.phphr_BA.phphr_HR.phphsb.phphsb_DE.phpht.phpht_HT.phphu.phphu_HU.phphy.phphy_AM.phpi18n.phpia.phpia_FR.phpid.phpid_ID.phpig.phpig_NG.phpii.phpik.phpik_CA.phpin.phpis.phpis_IS.phpit.phpit_CH.phpit_IT.phpit_SM.phpit_VA.phpiu.phpiu_CA.phpiw.phpja.phpja_JP.phpjgo.phpjmc.phpjv.phpka.phpka_GE.phpkab.phpkab_DZ.phpkam.phpkde.phpkea.phpkhq.phpki.phpkk.phpkk_KZ.phpkkj.phpkl.phpkl_GL.phpkln.phpkm.phpkm_KH.phpkn.phpkn_IN.phpko.phpko_KP.phpko_KR.phpkok.phpkok_IN.phpks.phpks_IN.phpks_IN@devanagari.phpksb.phpksf.phpksh.phpku.phpku_TR.phpkw.phpkw_GB.phpky.phpky_KG.phplag.phplb.phplb_LU.phplg.phplg_UG.phpli.phpli_NL.phplij.phplij_IT.phplkt.phpln.phpln_AO.phpln_CD.phpln_CF.phpln_CG.phplo.phplo_LA.phplrc.phplrc_IQ.phplt.phplt_LT.phplu.phpluo.phpluy.phplv.phplv_LV.phplzh.phplzh_TW.phpmag.phpmag_IN.phpmai.phpmai_IN.phpmas.phpmas_TZ.phpmer.phpmfe.phpmfe_MU.phpmg.phpmg_MG.phpmgh.phpmgo.phpmhr.phpmhr_RU.phpmi.phpmi_NZ.phpmiq.phpmiq_NI.phpmjw.phpmjw_IN.phpmk.phpmk_MK.phpml.phpml_IN.phpmn.phpmn_MN.phpmni.phpmni_IN.phpmo.phpmr.phpmr_IN.phpms.phpms_BN.phpms_MY.phpms_SG.phpmt.phpmt_MT.phpmua.phpmy.phpmy_MM.phpmzn.phpnan.phpnan_TW.phpnan_TW@latin.phpnaq.phpnb.phpnb_NO.phpnb_SJ.phpnd.phpnds.phpnds_DE.phpnds_NL.phpne.phpne_IN.phpne_NP.phpnhn.phpnhn_MX.phpniu.phpniu_NU.phpnl.phpnl_AW.phpnl_BE.phpnl_BQ.phpnl_CW.phpnl_NL.phpnl_SR.phpnl_SX.phpnmg.phpnn.phpnn_NO.phpnnh.phpno.phpnr.phpnr_ZA.phpnso.phpnso_ZA.phpnus.phpnyn.phpoc.phpoc_FR.phpom.phpom_ET.phpom_KE.phpor.phpor_IN.phpos.phpos_RU.phppa.phppa_Arab.phppa_Guru.phppa_IN.phppa_PK.phppap.phppap_AW.phppap_CW.phppl.phppl_PL.phpprg.phpps.phpps_AF.phppt.phppt_AO.phppt_BR.phppt_CH.phppt_CV.phppt_GQ.phppt_GW.phppt_LU.phppt_MO.phppt_MZ.phppt_PT.phppt_ST.phppt_TL.phpqu.phpqu_BO.phpqu_EC.phpquz.phpquz_PE.phpraj.phpraj_IN.phprm.phprn.phpro.phpro_MD.phpro_RO.phprof.phpru.phpru_BY.phpru_KG.phpru_KZ.phpru_MD.phpru_RU.phpru_UA.phprw.phprw_RW.phprwk.phpsa.phpsa_IN.phpsah.phpsah_RU.phpsaq.phpsat.phpsat_IN.phpsbp.phpsc.phpsc_IT.phpsd.phpsd_IN.phpsd_IN@devanagari.phpse.phpse_FI.phpse_NO.phpse_SE.phpseh.phpses.phpsg.phpsgs.phpsgs_LT.phpsh.phpshi.phpshi_Latn.phpshi_Tfng.phpshn.phpshn_MM.phpshs.phpshs_CA.phpsi.phpsi_LK.phpsid.phpsid_ET.phpsk.phpsk_SK.phpsl.phpsl_SI.phpsm.phpsm_WS.phpsmn.phpsn.phpso.phpso_DJ.phpso_ET.phpso_KE.phpso_SO.phpsq.phpsq_AL.phpsq_MK.phpsq_XK.phpsr.phpsr_Cyrl.phpsr_Cyrl_BA.phpsr_Cyrl_ME.phpsr_Cyrl_XK.phpsr_Latn.phpsr_Latn_BA.phpsr_Latn_ME.phpsr_Latn_XK.phpsr_ME.phpsr_RS.phpsr_RS@latin.phpss.phpss_ZA.phpst.phpst_ZA.phpsv.phpsv_AX.phpsv_FI.phpsv_SE.phpsw.phpsw_CD.phpsw_KE.phpsw_TZ.phpsw_UG.phpszl.phpszl_PL.phpta.phpta_IN.phpta_LK.phpta_MY.phpta_SG.phptcy.phptcy_IN.phpte.phpte_IN.phpteo.phpteo_KE.phptet.phptg.phptg_TJ.phpth.phpth_TH.phpthe.phpthe_NP.phpti.phpti_ER.phpti_ET.phptig.phptig_ER.phptk.phptk_TM.phptl.phptl_PH.phptlh.phptn.phptn_ZA.phpto.phpto_TO.phptpi.phptpi_PG.phptr.phptr_CY.phptr_TR.phpts.phpts_ZA.phptt.phptt_RU.phptt_RU@iqtelif.phptwq.phptzl.phptzm.phptzm_Latn.phpug.phpug_CN.phpuk.phpuk_UA.phpunm.phpunm_US.phpur.phpur_IN.phpur_PK.phpuz.phpuz_Arab.phpuz_Cyrl.phpuz_Latn.phpuz_UZ.phpuz_UZ@cyrillic.phpvai.phpvai_Latn.phpvai_Vaii.phpve.phpve_ZA.phpvi.phpvi_VN.phpvo.phpvun.phpwa.phpwa_BE.phpwae.phpwae_CH.phpwal.phpwal_ET.phpwo.phpwo_SN.phpxh.phpxh_ZA.phpxog.phpyav.phpyi.phpyi_US.phpyo.phpyo_BJ.phpyo_NG.phpyue.phpyue_HK.phpyue_Hans.phpyue_Hant.phpyuw.phpyuw_PG.phpzgh.phpzh.phpzh_CN.phpzh_HK.phpzh_Hans.phpzh_Hans_HK.phpzh_Hans_MO.phpzh_Hans_SG.phpzh_Hant.phpzh_Hant_HK.phpzh_Hant_MO.phpzh_Hant_TW.phpzh_MO.phpzh_SG.phpzh_TW.phpzh_YUE.phpzu.phpzu_ZA.php
Laravel
List
MessageFormatter
PHPStan
Traits
Translator.phpTranslatorImmutable.phpTranslatorStrongTypeInterface.php
nyholm
pablo-sg-pacheco
paragonie
phpdocumentor
reflection-common
reflection-docblock
type-resolver
phpseclib
bcmath_compat
phpseclib
AUTHORS
phpseclib
Common
Functions
Crypt
AES.phpBlowfish.phpChaCha20.php
Common
DES.phpDH.php
DH
DSA.php
DSA
EC.php
EC
Hash.phpPublicKeyLoader.phpRC2.phpRC4.phpRSA.php
RSA
Random.phpRijndael.phpSalsa20.phpTripleDES.phpTwofish.php
Exception
File
ANSI.phpASN1.php
ASN1
Element.php
Maps
AccessDescription.phpAdministrationDomainName.phpAlgorithmIdentifier.phpAnotherName.phpAttribute.phpAttributeType.phpAttributeTypeAndValue.phpAttributeValue.phpAttributes.phpAuthorityInfoAccessSyntax.phpAuthorityKeyIdentifier.phpBaseDistance.phpBasicConstraints.phpBuiltInDomainDefinedAttribute.phpBuiltInDomainDefinedAttributes.phpBuiltInStandardAttributes.phpCPSuri.phpCRLDistributionPoints.phpCRLNumber.phpCRLReason.phpCertPolicyId.phpCertificate.phpCertificateIssuer.phpCertificateList.phpCertificatePolicies.phpCertificateSerialNumber.phpCertificationRequest.phpCertificationRequestInfo.phpCharacteristic_two.phpCountryName.phpCurve.phpDHParameter.phpDSAParams.phpDSAPrivateKey.phpDSAPublicKey.phpDigestInfo.phpDirectoryString.phpDisplayText.phpDistributionPoint.phpDistributionPointName.phpDssSigValue.phpECParameters.phpECPoint.phpECPrivateKey.phpEDIPartyName.phpEcdsaSigValue.phpEncryptedData.phpEncryptedPrivateKeyInfo.phpExtKeyUsageSyntax.phpExtension.phpExtensionAttribute.phpExtensionAttributes.phpExtensions.phpFieldElement.phpFieldID.phpGeneralName.phpGeneralNames.phpGeneralSubtree.phpGeneralSubtrees.phpHashAlgorithm.phpHoldInstructionCode.phpInvalidityDate.phpIssuerAltName.phpIssuingDistributionPoint.phpKeyIdentifier.phpKeyPurposeId.phpKeyUsage.phpMaskGenAlgorithm.phpName.phpNameConstraints.phpNetworkAddress.phpNoticeReference.phpNumericUserIdentifier.phpORAddress.phpOneAsymmetricKey.phpOrganizationName.phpOrganizationalUnitNames.phpOtherPrimeInfo.phpOtherPrimeInfos.phpPBEParameter.phpPBES2params.phpPBKDF2params.phpPBMAC1params.phpPKCS9String.phpPentanomial.phpPersonalName.phpPolicyInformation.phpPolicyMappings.phpPolicyQualifierId.phpPolicyQualifierInfo.phpPostalAddress.phpPrime_p.phpPrivateDomainName.phpPrivateKey.phpPrivateKeyInfo.phpPrivateKeyUsagePeriod.phpPublicKey.phpPublicKeyAndChallenge.phpPublicKeyInfo.phpRC2CBCParameter.phpRDNSequence.phpRSAPrivateKey.phpRSAPublicKey.phpRSASSA_PSS_params.phpReasonFlags.phpRelativeDistinguishedName.phpRevokedCertificate.phpSignedPublicKeyAndChallenge.phpSpecifiedECDomain.phpSubjectAltName.phpSubjectDirectoryAttributes.phpSubjectInfoAccessSyntax.phpSubjectPublicKeyInfo.phpTBSCertList.phpTBSCertificate.phpTerminalIdentifier.phpTime.phpTrinomial.phpUniqueIdentifier.phpUserNotice.phpValidity.phpnetscape_ca_policy_url.phpnetscape_cert_type.phpnetscape_comment.php
X509.php
Math
Net
System
bootstrap.phpopenssl.cnf
phpstan
phpdoc-parser
phpstan-baseline.neon
src
Ast
AbstractNodeVisitor.phpAttribute.php
ConstExpr
Node.phpNodeAttributes.phpNodeTraverser.phpNodeVisitor.php
NodeVisitor
PhpDoc
Type
Lexer
Parser
Printer
psr
rakit
ralouphie
getallheaders
remotelyliving
rpnzl
scoper-autoload.php
scssphp
setasign
spatie
stripe
stripe-php
OPENAPI_VERSIONVERSION
data
init.php
lib
Account.phpAccountLink.php
ApiOperations
ApiRequestor.phpApiResource.phpApiResponse.phpApplePayDomain.phpApplicationFee.phpApplicationFeeRefund.php
Apps
Balance.phpBalanceTransaction.phpBankAccount.phpBaseStripeClient.phpBaseStripeClientInterface.php
BillingPortal
Capability.phpCard.phpCashBalance.phpCharge.php
Checkout
Collection.phpCountrySpec.phpCoupon.phpCreditNote.phpCreditNoteLineItem.phpCustomer.phpCustomerBalanceTransaction.phpCustomerCashBalanceTransaction.phpDiscount.phpDispute.phpEphemeralKey.phpErrorObject.phpEvent.php
Exception
ExchangeRate.phpFile.phpFileLink.php
FinancialConnections
FundingInstructions.php
HttpClient
Identity
Invoice.phpInvoiceItem.phpInvoiceLineItem.php
Issuing
LineItem.phpLoginLink.phpMandate.phpOAuth.phpOAuthErrorObject.phpPaymentIntent.phpPaymentLink.phpPaymentMethod.phpPayout.phpPerson.phpPlan.phpPrice.phpProduct.phpPromotionCode.phpQuote.php
Radar
RecipientTransfer.phpRefund.php
Reporting
RequestTelemetry.phpReview.phpSearchResult.php
Service
AbstractService.phpAbstractServiceFactory.phpAccountLinkService.phpAccountService.phpApplePayDomainService.phpApplicationFeeService.php
Apps
BalanceService.phpBalanceTransactionService.php
BillingPortal
ChargeService.php
Checkout
CoreServiceFactory.phpCountrySpecService.phpCouponService.phpCreditNoteService.phpCustomerService.phpDisputeService.phpEphemeralKeyService.phpEventService.phpExchangeRateService.phpFileLinkService.phpFileService.php
FinancialConnections
Identity
InvoiceItemService.phpInvoiceService.php
Issuing
MandateService.phpOAuthService.phpPaymentIntentService.phpPaymentLinkService.phpPaymentMethodService.phpPayoutService.phpPlanService.phpPriceService.phpProductService.phpPromotionCodeService.phpQuoteService.php
Radar
RefundService.php
Reporting
ReviewService.phpSetupAttemptService.phpSetupIntentService.phpShippingRateService.php
Sigma
SourceService.phpSubscriptionItemService.phpSubscriptionScheduleService.phpSubscriptionService.php
Tax
TaxCodeService.phpTaxRateService.php
Terminal
TestHelpers
TokenService.phpTopupService.phpTransferService.php
Treasury
WebhookEndpointService.php
SetupAttempt.phpSetupIntent.phpShippingRate.php
Sigma
SingletonApiResource.phpSource.phpSourceTransaction.phpStripe.phpStripeClient.phpStripeClientInterface.phpStripeObject.phpStripeStreamingClientInterface.phpSubscription.phpSubscriptionItem.phpSubscriptionSchedule.php
Tax
TaxCode.phpTaxId.phpTaxRate.php
Terminal
TestHelpers
Token.phpTopup.phpTransfer.phpTransferReversal.php
Treasury
UsageRecord.phpUsageRecordSummary.php
Util
Webhook.phpWebhookEndpoint.phpWebhookSignature.php
symfony
cache-contracts
cache
deprecation-contracts
event-dispatcher-contracts
event-dispatcher
polyfill-mbstring
polyfill-php73
polyfill-php80
polyfill-php81
process
service-contracts
translation-contracts
translation
Catalogue
CatalogueMetadataAwareInterface.php
Command
DataCollector
DataCollectorTranslator.php
DependencyInjection
Dumper
Exception
Extractor
Formatter
IdentityTranslator.php
Loader
LocaleSwitcher.phpLoggingTranslator.phpMessageCatalogue.phpMessageCatalogueInterface.phpMetadataAwareInterface.php
Provider
PseudoLocalizationTranslator.php
Reader
Resources
Test
TranslatableMessage.phpTranslator.phpTranslatorBag.phpTranslatorBagInterface.php
Util
Writer
var-exporter
webmozart
woocommerce
action-scheduler
action-scheduler.php
classes
ActionScheduler_ActionClaim.phpActionScheduler_ActionFactory.phpActionScheduler_AdminView.phpActionScheduler_AsyncRequest_QueueRunner.phpActionScheduler_Compatibility.phpActionScheduler_DataController.phpActionScheduler_DateTime.phpActionScheduler_Exception.phpActionScheduler_FatalErrorMonitor.phpActionScheduler_InvalidActionException.phpActionScheduler_ListTable.phpActionScheduler_LogEntry.phpActionScheduler_NullLogEntry.phpActionScheduler_OptionLock.phpActionScheduler_QueueCleaner.phpActionScheduler_QueueRunner.phpActionScheduler_Versions.phpActionScheduler_WPCommentCleaner.phpActionScheduler_wcSystemStatus.php
WP_CLI
abstracts
actions
data-stores
migration
schedules
schema
deprecated
functions.php
lib
yahnis-elsts
inc
admin-pages
class-about-admin-page.phpclass-addons-admin-page.phpclass-base-admin-page.phpclass-base-customer-facing-admin-page.phpclass-broadcast-edit-admin-page.phpclass-broadcast-list-admin-page.phpclass-checkout-form-edit-admin-page.phpclass-checkout-form-list-admin-page.phpclass-customer-edit-admin-page.phpclass-customer-list-admin-page.phpclass-customizer-admin-page.phpclass-dashboard-admin-page.phpclass-discount-code-edit-admin-page.phpclass-discount-code-list-admin-page.phpclass-domain-edit-admin-page.phpclass-domain-list-admin-page.phpclass-edit-admin-page.phpclass-email-edit-admin-page.phpclass-email-list-admin-page.phpclass-email-template-customize-admin-page.phpclass-event-list-admin-page.phpclass-event-view-admin-page.phpclass-hosting-integration-wizard-admin-page.phpclass-invoice-template-customize-admin-page.phpclass-jobs-list-admin-page.phpclass-list-admin-page.phpclass-membership-edit-admin-page.phpclass-membership-list-admin-page.phpclass-migration-alert-admin-page.phpclass-payment-edit-admin-page.phpclass-payment-list-admin-page.phpclass-placeholders-admin-page.phpclass-product-edit-admin-page.phpclass-product-list-admin-page.phpclass-rollback-admin-page.phpclass-settings-admin-page.phpclass-setup-wizard-admin-page.phpclass-shortcodes-admin-page.phpclass-site-edit-admin-page.phpclass-site-list-admin-page.phpclass-system-info-admin-page.phpclass-tax-rates-admin-page.phpclass-template-previewer-customize-admin-page.phpclass-top-admin-nav-menu.phpclass-view-logs-admin-page.phpclass-webhook-edit-admin-page.phpclass-webhook-list-admin-page.phpclass-wizard-admin-page.php
customer-panel
debug
api
builders
checkout
class-cart.phpclass-checkout-pages.phpclass-checkout.phpclass-legacy-checkout.phpclass-line-item.php
signup-fields
class-admin-notices.phpclass-admin-themes-compatibility.phpclass-ajax.phpclass-api.phpclass-async-calls.phpclass-autoloader.phpclass-core-updates.phpclass-cron.phpclass-current.phpclass-dashboard-statistics.phpclass-dashboard-widgets.phpclass-documentation.phpclass-domain-mapping.phpclass-faker.phpclass-geolocation.phpclass-helper.phpclass-hooks.phpclass-license.phpclass-light-ajax.phpclass-logger.phpclass-maintenance-mode.phpclass-requirements.phpclass-scripts.phpclass-session-cookie.phpclass-settings.phpclass-sunrise.phpclass-unsupported.phpclass-user-switching.phpclass-views.phpclass-whitelabel.phpclass-wp-ultimo.php
compat
contracts
country
br
ca
class-country-br.phpclass-country-ca.phpclass-country-cn.phpclass-country-de.phpclass-country-default.phpclass-country-es.phpclass-country-fr.phpclass-country-gb.phpclass-country-in.phpclass-country-jp.phpclass-country-mx.phpclass-country-my.phpclass-country-ne.phpclass-country-nl.phpclass-country-ru.phpclass-country-sg.phpclass-country-tr.phpclass-country-us.phpclass-country-za.phpclass-country.php
cn
de
es
fr
gb
in
jp
mx
my
ne
nl
ru
sg
tr
us
za
database
debug
deprecated
development
domain-mapping
duplication
exception
functions
gateways
helpers
installers
integrations
internal
invoices
limitations
limits
list-tables
loaders
managers
mercator
models
next
objects
rollback
site-templates
sso
tax
traits
ui
updater
lang
loco.xmlreadme.txtsunrise.phpuninstall.php
views
about.phpadmin-notices.php
admin-pages
base
broadcast
checkout
classes.php
customers
dashboard-statistics
dashboard-widgets
domain
dynamic-styles
email
emails
events
invoice
legacy
limitations
memberships
payments
phpcs.xml
rollback
settings
shortcodes
sites
system-info
taxes
ui
wizards
wp-multisite-waas.php

@ -0,0 +1,825 @@
<?php
/**
* Base Gateway.
*
* Base Gateway class. Should be extended to add new payment gateways.
*
* @package WP_Ultimo
* @subpackage Managers/Site_Manager
* @since 2.0.0
*/
namespace WP_Ultimo\Gateways;
use \WP_Ultimo\Database\Payments\Payment_Status;
use \WP_Ultimo\Database\Memberships\Membership_Status;
use \WP_Ultimo\Gateways\Base_Stripe_Gateway;
use \WP_Ultimo\Dependencies\Stripe;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Base Gateway class. Should be extended to add new payment gateways.
*
* @since 2.0.0
*/
class Stripe_Gateway extends Base_Stripe_Gateway {
/**
* Holds the ID of a given gateway.
*
* @since 2.0.0
* @var string
*/
protected $id = 'stripe';
/**
* Adds additional hooks.
*
* @since 2.0.0
* @return void
*/
public function hooks() {
parent::hooks();
add_filter('wu_customer_payment_methods', function($fields, $customer): array {
$this->customer = $customer;
$extra_fields = $this->payment_methods();
return array_merge($fields, $extra_fields);
}, 10, 2);
} // end hooks;
/**
* Adds the Stripe Gateway settings to the settings screen.
*
* @since 2.0.0
* @return void
*/
public function settings() {
$error_message_wrap = '<span class="wu-p-2 wu-bg-red-100 wu-text-red-600 wu-rounded wu-mt-3 wu-mb-0 wu-block wu-text-xs">%s</span>';
wu_register_settings_field('payment-gateways', 'stripe_header', array(
'title' => __('Stripe', 'wp-ultimo'),
'desc' => __('Use the settings section below to configure Stripe as a payment method.', 'wp-ultimo'),
'type' => 'header',
'show_as_submenu' => true,
'require' => array(
'active_gateways' => 'stripe',
),
));
wu_register_settings_field('payment-gateways', 'stripe_public_title', array(
'title' => __('Stripe Public Name', 'wp-ultimo'),
'tooltip' => __('The name to display on the payment method selection field. By default, "Credit Card" is used.', 'wp-ultimo'),
'type' => 'text',
'default' => __('Credit Card', 'wp-ultimo'),
'require' => array(
'active_gateways' => 'stripe',
),
));
wu_register_settings_field('payment-gateways', 'stripe_sandbox_mode', array(
'title' => __('Stripe Sandbox Mode', 'wp-ultimo'),
'desc' => __('Toggle this to put Stripe on sandbox mode. This is useful for testing and making sure Stripe is correctly setup to handle your payments.', 'wp-ultimo'),
'type' => 'toggle',
'default' => 1,
'html_attr' => array(
'v-model' => 'stripe_sandbox_mode',
),
'require' => array(
'active_gateways' => 'stripe',
),
));
$pk_test_status = wu_get_setting('stripe_test_pk_key_status', '');
wu_register_settings_field('payment-gateways', 'stripe_test_pk_key', array(
'title' => __('Stripe Test Publishable Key', 'wp-ultimo'),
'desc' => !empty($pk_test_status) ? sprintf($error_message_wrap, $pk_test_status) : '',
'tooltip' => __('Make sure you are placing the TEST keys, not the live ones.', 'wp-ultimo'),
'placeholder' => __('pk_test_***********', 'wp-ultimo'),
'type' => 'text',
'default' => '',
'capability' => 'manage_api_keys',
'require' => array(
'active_gateways' => 'stripe',
'stripe_sandbox_mode' => 1,
),
));
$sk_test_status = wu_get_setting('stripe_test_sk_key_status', '');
wu_register_settings_field('payment-gateways', 'stripe_test_sk_key', array(
'title' => __('Stripe Test Secret Key', 'wp-ultimo'),
'desc' => !empty($sk_test_status) ? sprintf($error_message_wrap, $sk_test_status) : '',
'tooltip' => __('Make sure you are placing the TEST keys, not the live ones.', 'wp-ultimo'),
'placeholder' => __('sk_test_***********', 'wp-ultimo'),
'type' => 'text',
'default' => '',
'capability' => 'manage_api_keys',
'require' => array(
'active_gateways' => 'stripe',
'stripe_sandbox_mode' => 1,
),
));
$pk_status = wu_get_setting('stripe_live_pk_key_status', '');
wu_register_settings_field('payment-gateways', 'stripe_live_pk_key', array(
'title' => __('Stripe Live Publishable Key', 'wp-ultimo'),
'desc' => !empty($pk_status) ? sprintf($error_message_wrap, $pk_status) : '',
'tooltip' => __('Make sure you are placing the LIVE keys, not the test ones.', 'wp-ultimo'),
'placeholder' => __('pk_live_***********', 'wp-ultimo'),
'type' => 'text',
'default' => '',
'capability' => 'manage_api_keys',
'require' => array(
'active_gateways' => 'stripe',
'stripe_sandbox_mode' => 0,
),
));
$sk_status = wu_get_setting('stripe_live_sk_key_status', '');
wu_register_settings_field('payment-gateways', 'stripe_live_sk_key', array(
'title' => __('Stripe Live Secret Key', 'wp-ultimo'),
'desc' => !empty($sk_status) ? sprintf($error_message_wrap, $sk_status) : '',
'tooltip' => __('Make sure you are placing the LIVE keys, not the test ones.', 'wp-ultimo'),
'placeholder' => __('sk_live_***********', 'wp-ultimo'),
'type' => 'text',
'default' => '',
'capability' => 'manage_api_keys',
'require' => array(
'active_gateways' => 'stripe',
'stripe_sandbox_mode' => 0,
),
));
$webhook_message = sprintf('<span class="wu-p-2 wu-bg-blue-100 wu-text-blue-600 wu-rounded wu-mt-3 wu-mb-0 wu-block wu-text-xs">%s</span>', __('Whenever you change your Stripe settings, WP Ultimo will automatically check the webhook URLs on your Stripe account to make sure we get notified about changes in subscriptions and payments.', 'wp-ultimo'));
wu_register_settings_field('payment-gateways', 'stripe_webhook_listener_explanation', array(
'title' => __('Webhook Listener URL', 'wp-ultimo'),
'desc' => $webhook_message,
'tooltip' => __('This is the URL Stripe should send webhook calls to.', 'wp-ultimo'),
'type' => 'text-display',
'copy' => true,
'default' => $this->get_webhook_listener_url(),
'wrapper_classes' => '',
'require' => array(
'active_gateways' => 'stripe',
),
));
parent::settings();
} // end settings;
/**
* Run preparations before checkout processing.
*
* This runs during the checkout form validation
* and it is a great chance to do preflight stuff
* if the gateway requires it.
*
* If you return an array here, Ultimo
* will append the key => value of that array
* as hidden fields to the checkout field,
* and those get submitted with the rest of the form.
*
* As an example, this is how we create payment
* intents for Stripe to make the experience more
* streamlined.
*
* @since 2.0.0
* @return void|array
*/
public function run_preflight() {
/*
* This is the stripe preflight code.
*
* Stripe requires us to create a payment intent
* or payment setup to be able to charge customers.
*
* This is done in order to comply with EU SCA
* and other such regulations.
*
* Before we get started, we need to get our stripe
* customer.
*/
$s_customer = $this->get_or_create_customer($this->customer->get_id(), $this->customer->get_user_id());
/*
* Things can go wrong,
* check for WP_Error.
*/
if (is_wp_error($s_customer)) {
// translators: %s is the error message.
return new \WP_Error($s_customer->get_error_code(), sprintf(__('Error creating Stripe customer: %s', 'wp-ultimo'), $s_customer->get_error_message()));
} // end if;
$this->membership->set_gateway_customer_id($s_customer->id);
$this->membership->set_gateway($this->get_id());
$type = $this->order->get_cart_type();
/*
* Lets deal with upgrades, downgrades and addons
*
* Here, we just need to make sure we process
* a membership swap.
*/
if ($type === 'upgrade' || $type === 'addon') {
$this->membership->swap($this->order);
} elseif ($type === 'downgrade') {
$this->membership->schedule_swap($this->order);
} // end if;
$this->membership->save();
$intent_args = array(
'customer' => $s_customer->id,
'metadata' => $this->get_customer_metadata(),
'description' => $this->order->get_cart_descriptor(),
);
/*
* Maybe use an existing payment method.
*/
if (wu_request('payment_method', 'add-new') !== 'add-new') {
$intent_args['payment_method'] = sanitize_text_field(wu_request('payment_method'));
} // end if;
/*
* Let's start with the intent options.
*
* We'll append the extra options as we go.
* This should also be filterable, to allow support
* for Stripe Connect in the future.
*/
$intent_options = array();
/*
* Tries to retrieve an existing intent id,
* from the current payment.
*/
$payment_intent_id = $this->payment->get_meta('stripe_payment_intent_id');
$existing_intent = false;
/**
* Ensure the correct api keys are set
*/
$this->setup_api_keys();
/*
* Tries to retrieve an intent on Stripe.
*
* If we success, we update it, if we fail,
* we try to create a new one.
*/
try {
/*
* Payment intents are used when we have an initial
* payment attached to the membership. Those start with a pi_
* id.
*/
if (!empty($payment_intent_id) && strncmp((string) $payment_intent_id, 'pi_', strlen('pi_')) === 0) {
$existing_intent = Stripe\PaymentIntent::retrieve($payment_intent_id);
/*
* Setup intents are created with the intent
* of future charging. This is what we use
* when we set up a subscription without a
* initial amount.
*/
} elseif (!empty($payment_intent_id) && strncmp((string) $payment_intent_id, 'seti_', strlen('seti_')) === 0) {
$existing_intent = Stripe\SetupIntent::retrieve($payment_intent_id);
} // end if;
/*
* We can't use cancelled intents
* for obvious reasons...
*/
if (!empty($existing_intent) && 'canceled' === $existing_intent->status) {
$existing_intent = false;
} // end if;
/*
* If we have a initial payment,
* we need to take care of that logic.
*
* If we have a trial, we need to deal with that via a setup intent.
*/
if ($this->order->get_total() && $this->order->has_trial() === false) {
$intent_args = wp_parse_args($intent_args, array(
'amount' => $this->order->get_total() * wu_stripe_get_currency_multiplier(),
'confirmation_method' => 'automatic',
'setup_future_usage' => 'off_session',
'currency' => strtolower((string) wu_get_setting('currency_symbol', 'USD')),
'confirm' => false,
));
/**
* Filters the payment intent arguments.
*
* @since 2.0
*
* @param array $intent_args The list of intent args.
* @param \WP_Ultimo\Gateways\Stripe_Gateway $this.
* @return array
*/
$intent_args = apply_filters('wu_stripe_create_payment_intent_args', $intent_args, $this);
if (!empty($existing_intent) && 'payment_intent' === $existing_intent->object) {
$idempotency_args = $intent_args;
$idempotency_args['update'] = true;
/*
* Stripe allows us to send a key
* together with the arguments to prevent
* duplication in payment intents.
*
* Same parameters = same key,
* so Stripe knows what to ignore.
*/
$intent_options['idempotency_key'] = wu_stripe_generate_idempotency_key($idempotency_args);
// Unset some options we can't update.
$unset_args = array('confirmation_method', 'confirm');
foreach ($unset_args as $unset_arg) {
if (isset($intent_args[$unset_arg])) {
unset($intent_args[$unset_arg]);
} // end if;
} // end foreach;
/*
* Tries to update the payment intent.
*/
$intent = Stripe\PaymentIntent::update($existing_intent->id, $intent_args, $intent_options);
} else {
$intent_options['idempotency_key'] = wu_stripe_generate_idempotency_key($intent_args);
$intent = Stripe\PaymentIntent::create($intent_args, $intent_options);
} // end if;
} else {
/*
* Create a setup intent instead.
*/
$intent_args = wp_parse_args($intent_args, array(
'usage' => 'off_session'
));
if (empty($existing_intent) || 'setup_intent' !== $existing_intent->object) {
$intent_options['idempotency_key'] = wu_stripe_generate_idempotency_key($intent_args);
/*
* Tries to create in Stripe.
*/
$intent = Stripe\SetupIntent::create($intent_args, $intent_options);
} // end if;
} // end if;
} catch (Stripe\Stripe\Error\Base $e) {
return $this->get_stripe_error($e);
} catch (\Exception $e) {
$error_code = $e->getCode();
// WP Error did not handle empty error code
if (empty($error_code)) {
if (method_exists($e, 'getHttpStatus')) {
$error_code = $e->getHttpStatus();
} else {
$error_code = 500;
} // end if;
} // end if;
return new \WP_Error($error_code, $e->getMessage());
} // end try;
/*
* To prevent re-doing all this
* work again, we save the intent on
* the payment, so we can use it
* in cases a retry is needed.
*/
$this->payment->update_meta('stripe_payment_intent_id', sanitize_text_field($intent->id));
/*
* Anything returned in this array
* gets added to the checkout form as hidden
* fields just before the form submission.
*
* Here we pass the data we need from the
* recently create intents.
*
* Using this info, we'll be able to process
* the Stripe payment on the next step: process_checkout
*/
return array(
'stripe_client_secret' => sanitize_text_field($intent->client_secret),
'stripe_intent_type' => sanitize_text_field($intent->object),
);
} // end run_preflight;
/**
* Process a checkout.
*
* It takes the data concerning
* a new checkout and process it.
*
* @since 2.0.0
*
* @param \WP_Ultimo\Models\Payment $payment The payment associated with the checkout.
* @param \WP_Ultimo\Models\Membership $membership The membership.
* @param \WP_Ultimo\Models\Customer $customer The customer checking out.
* @param \WP_Ultimo\Checkout\Cart $cart The cart object.
* @param string $type The checkout type. Can be 'new', 'retry', 'upgrade', 'downgrade', 'addon'.
*
* @throws \Exception When a stripe API error is caught.
*
* @return void
*/
public function process_checkout($payment, $membership, $customer, $cart, $type) {
/*
* Here's the general idea
* of how the Stripe integration works.
*
* Despite of the type, we'll need to
* cancel an existing subscription to create
* a new one.
*
* Then, after that's all said and done
* we can move our attention back to handling
* the membership, payment, and customer locally.
*
* For sanity reasons, stripe variants of data type
* such as a Stripe\Customer instance, will be
* hold by variables stating with s_ (e.g. s_customer)
*
* First, we need to check for a valid payment intent.
*/
$payment_intent_id = $payment->get_meta('stripe_payment_intent_id');
if (empty($payment_intent_id)) {
throw new \Exception(__('Missing Stripe payment intent, please try again or contact support if the issue persists.', 'wp-ultimo'), 'missing_stripe_payment_intent');
} // end if;
/**
* Ensure the correct api keys are set
*/
$this->setup_api_keys();
/*
* To make our lives easier, let's
* set a couple of variables based on the order.
*/
$should_auto_renew = $cart->should_auto_renew();
$is_recurring = $cart->has_recurring();
$is_setup_intent = false;
/*
* Get the correct intent
* type depending on the intent ID
*/
if (strncmp((string) $payment_intent_id, 'seti_', strlen('seti_')) === 0) {
$is_setup_intent = true;
$payment_intent = Stripe\SetupIntent::retrieve($payment_intent_id);
} else {
$payment_intent = Stripe\PaymentIntent::retrieve($payment_intent_id);
} // end if;
/*
* Retrieves the Stripe Customer
* or create a new one!
*/
$s_customer = $this->get_or_create_customer($customer->get_id(), $customer->get_user_id(), $payment_intent->customer);
// translators: first is the customer id, then the customer email.
$description = sprintf(__('Customer ID: %1$d - User Email: %2$s', 'wp-ultimo'), $customer->get_id(), $customer->get_email_address());
if (strlen($description) > 350) {
$description = substr($description, 0, 350);
} // end if;
/*
* Updates the customer on Stripe
* to make sure it always has the most
* up-to-date info.
*/
Stripe\Customer::update($s_customer->id, array(
'address' => $this->convert_to_stripe_address($customer->get_billing_address()),
'description' => sanitize_text_field($description),
'metadata' => array(
'email' => $customer->get_email_address(),
'user_id' => $customer->get_user_id(),
'customer_id' => $customer->get_id(),
),
));
/*
* Persist payment methods.
*
* This is not really THAT mission
* critical, but it is a nice-to-have
* that being said, we'll have it happen
* on the sidelines.
*/
$payment_method = $this->save_payment_method($payment_intent, $s_customer);
$payment_completed = $is_setup_intent || (!empty($payment_intent->charges->data[0]['id']) && 'succeeded' === $payment_intent->charges->data[0]['status']);
$subscription = false;
if ($payment_completed && $should_auto_renew && $is_recurring) {
$subscription = $this->create_recurring_payment($membership, $cart, $payment_method, $s_customer);
if (!$subscription) {
/**
* Another process is already taking care of this (webhook).
*/
return;
} // end if;
} // end if;
if ($payment_completed) {
$payment_id = $is_setup_intent ? $payment_intent->id : sanitize_text_field($payment_intent->charges->data[0]['id']);
$payment->set_status(Payment_Status::COMPLETED);
$payment->set_gateway($this->get_id());
$payment->set_gateway_payment_id($payment_id);
$payment->save();
$this->trigger_payment_processed($payment, $membership);
} // end if;
if ($subscription) {
$membership->set_gateway($this->get_id());
$membership->set_gateway_customer_id($s_customer->id);
$membership->set_gateway_subscription_id($subscription->id);
$membership->add_to_times_billed(1);
$membership->should_auto_renew(true);
if ($type !== 'downgrade') {
$membership_status = $cart->has_trial() ? Membership_Status::TRIALING : Membership_Status::ACTIVE;
$renewal_date = new \DateTime();
$renewal_date->setTimestamp($subscription->current_period_end);
$renewal_date->setTime(23, 59, 59);
$stripe_estimated_charge_timestamp = $subscription->current_period_end + (2 * HOUR_IN_SECONDS);
if ($stripe_estimated_charge_timestamp > $renewal_date->getTimestamp()) {
$renewal_date->setTimestamp($stripe_estimated_charge_timestamp);
} // end if;
$expiration = $renewal_date->format('Y-m-d H:i:s');
$membership->renew(true, $membership_status, $expiration);
} else {
$membership->save();
} // end if;
} // end if;
} // end process_checkout;
/**
* Add credit card fields.
*
* @since 2.0.0
* @return string
*/
public function fields(): string {
$fields = array();
$card_options = $this->get_saved_card_options();
if ($card_options) {
$card_options['add-new'] = __('Add new card', 'wp-ultimo');
$fields = array(
'payment_method' => array(
'type' => 'radio',
'title' => __('Saved Payment Methods', 'wp-ultimo'),
'value' => wu_request('payment_method'),
'options' => $card_options,
'html_attr' => array(
'v-model' => 'payment_method',
),
)
);
} // end if;
$stripe_form = new \WP_Ultimo\UI\Form('billing-address-fields', $fields, array(
'views' => 'checkout/fields',
'variables' => array(
'step' => (object) array(
'classes' => '',
),
),
));
ob_start();
$stripe_form->render();
// phpcs:disable
?>
<div v-if="payment_method == 'add-new'">
<div id="card-element" class="wu-mb-4">
<!-- A Stripe Element will be inserted here. -->
</div>
<div class="" id="ideal-bank-element">
<!-- A Stripe iDEAL Element will be inserted here. -->
</div>
<!-- Used to display Element errors. -->
<div id="card-errors" role="alert"></div>
</div>
<?php
// phpcs:enable
return ob_get_clean();
} // end fields;
/**
* Returns the payment methods.
*
* @since 2.0.0
* @return array
*/
public function payment_methods() {
$fields = array();
$card_options = $this->get_saved_card_options();
if ($card_options) {
foreach ($card_options as $payment_method => $card) {
$fields = array(
"payment_method_{$payment_method}" => array(
'type' => 'text-display',
'title' => __('Saved Cards', 'wp-ultimo'),
'display_value' => $card,
)
);
} // end foreach;
} // end if;
return $fields;
} // end payment_methods;
/**
* Get the saved Stripe payment methods for a given user ID.
*
* @since 2.0.0
*
* @throws \Exception, When info is wrong.
* @throws \Exception When info is wrong 2.
* @return PaymentMethod[]|array
*/
public function get_user_saved_payment_methods() {
$customer = wu_get_current_customer();
if (!$customer) {
return array();
} // end if;
$customer_id = $customer->get_id();
try {
/*
* Declare static to prevent multiple calls.
*/
static $existing_payment_methods;
if (!is_null($existing_payment_methods) && array_key_exists($customer_id, $existing_payment_methods)) {
return $existing_payment_methods[$customer_id];
} // end if;
$customer_payment_methods = array();
$stripe_customer_id = \WP_Ultimo\Models\Membership::query(array(
'customer_id' => $customer_id,
'search' => 'cus_*',
'fields' => array('gateway_customer_id'),
));
$stripe_customer_id = current(array_column($stripe_customer_id, 'gateway_customer_id'));
/**
* Ensure the correct api keys are set
*/
$this->setup_api_keys();
$payment_methods = Stripe\PaymentMethod::all(array(
'customer' => $stripe_customer_id,
'type' => 'card'
));
foreach ($payment_methods->data as $payment_method) {
$customer_payment_methods[$payment_method->id] = $payment_method;
} // end foreach;
$existing_payment_methods[$customer_id] = $customer_payment_methods;
return $existing_payment_methods[$customer_id];
} catch (\Throwable $exception) {
return array();
} // end try;
} // end get_user_saved_payment_methods;
} // end class Stripe_Gateway;