From 3a0659b63b84c644a1c5a4e5f5ed4015c94169ef Mon Sep 17 00:00:00 2001 From: Kobe Date: Wed, 25 Jun 2025 17:14:03 +0200 Subject: [PATCH] added payment links to prices --- __pycache__/app.cpython-313.pyc | Bin 12425 -> 12425 bytes __pycache__/models.cpython-313.pyc | Bin 49701 -> 49929 bytes ...d_stripe_payment_links_to_pricing_plans.py | 43 ++++++++++++ models.py | 3 + routes/__pycache__/admin.cpython-313.pyc | Bin 34924 -> 35205 bytes routes/admin.py | 12 ++-- static/js/settings/pricing.js | 3 +- templates/components/pricing_section.html | 40 ++++++++++- templates/settings/tabs/pricing.html | 66 ++++++++++++++++-- 9 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 migrations/versions/add_stripe_payment_links_to_pricing_plans.py diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index 7da0fcdfd4479cf5dccc8c14117885c0b61f1d41..d7aab4932379084b44c246ea74de8fdeaac5aa3c 100644 GIT binary patch delta 23 dcmeB7>`dhQ%*)Hg00g>EqBFV_Hu6;)002`~2I2q! delta 23 dcmeB7>`dhQ%*)Hg00b%Ckr}JyH}X{*002mx}=i{xip9gja9mOJB-#i*@qD>B}c4XbMk$tI4_9YS}u*N%>-v z|CI7gS|JBzF*6lOOg7ZeXXgv%uw*KdoZKiTJy~vr)a1VtWhOtc6oHCMgVZqcP1c*F z2oeRd99M!_PC}+s%HTj^B zEH_&)yCrjxS`2HDACND~Dhy-=fLP%o!J@%pmP|2f`i#NiKsB1d5>gCBS~1$el2Qz& zAio1akq(eAg~ZnlmIkWQ3zh+r`oXe5(jZt4NE!yq7a651Xev&=xGIo6^9v(TFVkd! z*%Fg&R_0WAo-Yn?jfwCvN_@^&T_h z{LL43YB4ezPj-|PnH;cKe)0wp&dKJxQ#5Z0=jP{?WaL!F7nc-e7No}KWaeex5=zX= zD^1Kn7MnbK_X4Igrpb&;xkZ%4>QDW?l8 q;x{H=oF=(ha_>?mMzhWP_XjeGMlv!APEeYzF;U|y1BhJ&3JL(eq^H;b delta 448 zcmZ8cJ4-@g7(VZJva(Vvk7kydVtC0b6mg=VmWC+GzV0|Q1hoWm#KkEgf|khMprN3k zypUb_pr8_LZ;|$>AJ7!h($IG{=^Y-P+dDklTVifg)PL!8O#;8b{?gig`c}W1BXb)3 zI4Iz<&xoHsEgQ^V0lv#pgN`=JL}|y2xf4SL36oiu>Vpn5MQ2UJicxQvyPw5=j-kEU z%%*e*=XK!x=hx&Ky(}s&o=0I6ZxdZDw4OF_%N^HDkKuEhNz=yJ zxXR{@RH7!C#NA<_X5R78cB`Ox<34J!3jOL%eu|&#jvC!dt-KYWog9O-i(`n|IEJZR ziL5$&-8fr{5rv@P(1P(vJ-(KXVVxx}7620LojUMF(qiRt1t*?PNFPBsvtuOY$AL1k-*TQnA3LqY Uh-nU>b=P&^&3eBDl`HC!KVoKt{{R30 diff --git a/migrations/versions/add_stripe_payment_links_to_pricing_plans.py b/migrations/versions/add_stripe_payment_links_to_pricing_plans.py new file mode 100644 index 0000000..1ea20a9 --- /dev/null +++ b/migrations/versions/add_stripe_payment_links_to_pricing_plans.py @@ -0,0 +1,43 @@ +"""add stripe payment links to pricing plans + +Revision ID: add_stripe_payment_links +Revises: 9206bf87bb8e +Create Date: 2024-12-19 13:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy import text + + +# revision identifiers, used by Alembic. +revision = 'add_stripe_payment_links' +down_revision = '9206bf87bb8e' +branch_labels = None +depends_on = None + + +def upgrade(): + conn = op.get_bind() + + # Check if columns already exist + result = conn.execute(text(""" + SELECT column_name + FROM information_schema.columns + WHERE table_name = 'pricing_plans' + AND column_name IN ('monthly_stripe_link', 'annual_stripe_link') + """)) + existing_columns = [row[0] for row in result.fetchall()] + + # Add Stripe payment link columns if they don't exist + if 'monthly_stripe_link' not in existing_columns: + op.add_column('pricing_plans', sa.Column('monthly_stripe_link', sa.String(length=500), nullable=True)) + + if 'annual_stripe_link' not in existing_columns: + op.add_column('pricing_plans', sa.Column('annual_stripe_link', sa.String(length=500), nullable=True)) + + +def downgrade(): + # Remove Stripe payment link columns + op.drop_column('pricing_plans', 'annual_stripe_link') + op.drop_column('pricing_plans', 'monthly_stripe_link') \ No newline at end of file diff --git a/models.py b/models.py index 3a96298..e9a236c 100644 --- a/models.py +++ b/models.py @@ -603,6 +603,9 @@ class PricingPlan(db.Model): is_custom = db.Column(db.Boolean, default=False) button_text = db.Column(db.String(50), default='Get Started') button_url = db.Column(db.String(200), default='#') + # Stripe payment links + monthly_stripe_link = db.Column(db.String(500), nullable=True) + annual_stripe_link = db.Column(db.String(500), nullable=True) order_index = db.Column(db.Integer, default=0) is_active = db.Column(db.Boolean, default=True) # Quota fields diff --git a/routes/__pycache__/admin.cpython-313.pyc b/routes/__pycache__/admin.cpython-313.pyc index d7c90a3c9595b58f33c2fc89ff5c1f3447dd9225..4ca6cc13c5505920a2f41499b5e7f847eb7f32ce 100644 GIT binary patch delta 1362 zcmb7^-A`L(7{K569Qv_S3Y60VEj_0noOVOj!dQVgAOgZRbfd_jOjZy}#z$T0*r7F% zu+UzxEjkn3bFpkU^Wz`ToWyve!CiQxLUA+0g)Ly*OboiS3^T8$@4MQB#0%rOc%I*L zp65L8N&4&AeverKrsT2N%pr2c4I|UXl}}54?_>ImkkuW7+4e?tA^Z_jEb~na9YH9$ z2bKp8LTq3N4s#;7r>yYjR5|=5x2Zj{3Byyl9Wrqn^bgjWg$CS&^N-0QoFDwcP#(cv zB>Ul|$f(U3v5aBa6)Bh90Ww6`6Gc#Gau$u@W(=7j4?KxjF%QK8X*IUb&xl+!rOx`_;4tMt#I}b@9~A0ImU%#(&@BH|Kv$LiP~8gn#8qxfjjYC zfS);&Oid@=8kaH? zZIGMcV0&0#&a?1I>J)B;*^^i5Mc?(r)x>QvxF!a3_1(+kH^ttZv3f(0u8&+Dxh(|N zgg`FXwcK@6IFd6uRA)4V7x^;oM~h8kN#3fe(R3s~(xd8qnt_rLIsDolN*t;XH4~*` zWS2BEr4r-`X{D6Pkh@W{P%1}NUX7<@MU`&NMyY~QC8a9bwNrA?C#v}lf=(m`HG$p~ z>A@~aZhDA^kmf~p?+2aN%vuepb|h3KU0J-msQE~Bv1sQx(2Xj-maqeT<9jhg(yD{& zm)KRQso%xs?3i%Qr6Y0P4_j%!dSi5gExyBzr&Fmj$+5JOfJ`O;_c8`}JtpG~IG1UF zpJP3`d*mlEsljoU!4Dv#J5J?b#PdFltoDBJRa!dFx80yEWUu@_X4^@olUIZEKs zbi2ASO%x@7Maaf^;?^(mTC;1Hrfv}4_|g&KBW%J|&?`#h%3lLr5YzLx2iBh73!_6; z;c%f-Q1D-~y>!-$qc0aGBY8Qc|B%xMtEVTi6AmQe*bil?gRqk5B+dn(WkzIe%3XMM zrkU3IezJM4Afag5BxNJ{v(B0FCbqz9$*=#fiQtU>0l`_HRMULR=wA1RZW&$c4(U?q zy4`!JBp;rY-q6J&NY0+y*XKt!q}~H0eOu90O45p-3pcPU4Nc)XcGZUouh!|p4Jv;& z!s5FSPqlGBkym|_zA6jZ`3cpUF4mEbXWp*APdds2=$LQO{Yvotj~C|k&s_MP#QyC5 z%56IE0uA&lP^(iur~Dg0w$1X8pi0-al8`|=MzEX|)u-7i)Hqt(vo{14{x^(T#EaV-_)GG_-0(wAPNn(uE_H9lKjcu~tNHXx*@N;hnz$cxZhl delta 1287 zcmai!TTC2P7{||<*;)3&E_)kxx$O?Sv%oejwq?1L4$G|(s&=^yyA`&yS<-Gw1521? z#grIm6DrX*p?oG7(}&haHBmDUnke4l8!4nQN<|uNqESObr=c2yeefJ8O%qL=hyVBc zpL4!*=JL->19NYdv2I!{CPc2SJBhP-qaRu~qKv@+7q}N-c08%R+mT`5(WJp3>+9PQ zN*m|oD(LTyLt~GG-H;q~;#`RIG^kaw8N+N($X>i3S7W*%LUKu%5fOOfK9K62@Tlk2 z9D5A=ksO5AV*M6x%r5)(k#(oAEWlPQfE{FQmD#b|FYNSA#4A8Q;D^3>o-dw1arMLsAH2l}uMd6D`VC(< zV^VJ11H z$wtXe$${L0=A`7J#8b+rvu;WrBplQVC>0`arB>vqAy`b0E1`p4y0wpxCZGcU_2#!M zS}Ac4{m3KEPtHwh0pezK4=dn}ekJIR)}lqO^#CbLwWdzYF5~JBiCwP2lqHVn`7&6F zdcZbrRl81}VPT_J57B|Wa6j1x_j@_QqozCL;~5!*GM_?vPr0fMtYG{mOmWB5+3{iw zObpUROB zHqU*Qn~2a*(W|=&l6og@gBY$CCB{;Ri*_F8b_ z=r&R>*>nx{ z`c862W;bI-SW3IhKa!#S^v(UoJPBMiJQxVWrIES+4jtS``~lKvu&7YHoOgR$3jCL? zx3^{6`ol0i`by|nBl=1_YA0o}ro}+Y%|=s)kzF!Ycj(xq2qwI&$nLNLHowhVmIY=bL?d*9E-nl4o8)W2tb^T%sE73G5^u;LKH$rz;=(Ms( z4gKb2Z+xBvhE diff --git a/routes/admin.py b/routes/admin.py index b780b03..fbb43dc 100644 --- a/routes/admin.py +++ b/routes/admin.py @@ -469,7 +469,8 @@ def create_pricing_plan(): annual_price = float(request.form.get('annual_price')) features = json.loads(request.form.get('features', '[]')) button_text = request.form.get('button_text', 'Get Started') - button_url = request.form.get('button_url', '#') + monthly_stripe_link = request.form.get('monthly_stripe_link', '') + annual_stripe_link = request.form.get('annual_stripe_link', '') is_popular = request.form.get('is_popular') == 'true' is_custom = request.form.get('is_custom') == 'true' is_active = request.form.get('is_active') == 'true' @@ -496,7 +497,8 @@ def create_pricing_plan(): annual_price=annual_price, features=features, button_text=button_text, - button_url=button_url, + monthly_stripe_link=monthly_stripe_link, + annual_stripe_link=annual_stripe_link, is_popular=is_popular, is_custom=is_custom, is_active=is_active, @@ -582,7 +584,8 @@ def update_pricing_plan(plan_id): annual_price = float(request.form.get('annual_price')) features = json.loads(request.form.get('features', '[]')) button_text = request.form.get('button_text', 'Get Started') - button_url = request.form.get('button_url', '#') + monthly_stripe_link = request.form.get('monthly_stripe_link', '') + annual_stripe_link = request.form.get('annual_stripe_link', '') is_popular = request.form.get('is_popular') == 'true' is_custom = request.form.get('is_custom') == 'true' is_active = request.form.get('is_active') == 'true' @@ -605,7 +608,8 @@ def update_pricing_plan(plan_id): plan.annual_price = annual_price plan.features = features plan.button_text = button_text - plan.button_url = button_url + plan.monthly_stripe_link = monthly_stripe_link + plan.annual_stripe_link = annual_stripe_link plan.is_popular = is_popular plan.is_custom = is_custom plan.is_active = is_active diff --git a/static/js/settings/pricing.js b/static/js/settings/pricing.js index 653b2b6..909d20f 100644 --- a/static/js/settings/pricing.js +++ b/static/js/settings/pricing.js @@ -164,7 +164,8 @@ function loadPlanForEdit(planId) { document.getElementById('editMonthlyPrice').value = plan.monthly_price; document.getElementById('editAnnualPrice').value = plan.annual_price; document.getElementById('editButtonText').value = plan.button_text; - document.getElementById('editButtonUrl').value = plan.button_url; + document.getElementById('editMonthlyStripeLink').value = plan.monthly_stripe_link || ''; + document.getElementById('editAnnualStripeLink').value = plan.annual_stripe_link || ''; document.getElementById('editIsPopular').checked = plan.is_popular; document.getElementById('editIsCustom').checked = plan.is_custom; document.getElementById('editIsActive').checked = plan.is_active; diff --git a/templates/components/pricing_section.html b/templates/components/pricing_section.html index 2f63494..473e9a8 100644 --- a/templates/components/pricing_section.html +++ b/templates/components/pricing_section.html @@ -47,9 +47,27 @@ - - {{ plan.button_text }} - + + {% if plan.is_custom %} + + {{ plan.button_text }} + + {% else %} + {% if plan.monthly_stripe_link or plan.annual_stripe_link %} + + {{ plan.button_text }} + + {% else %} + + {{ plan.button_text }} + + {% endif %} + {% endif %} @@ -242,6 +260,14 @@ document.addEventListener('DOMContentLoaded', function() { // Simply animate the number change animateNumber(price, monthlyValue, annualValue); }); + + // Update payment links to annual + document.querySelectorAll('.payment-button').forEach(button => { + const annualLink = button.getAttribute('data-annual-link'); + if (annualLink) { + button.href = annualLink; + } + }); } else { // Switch to monthly prices with animation monthlyPrices.forEach((price, index) => { @@ -251,6 +277,14 @@ document.addEventListener('DOMContentLoaded', function() { // Simply animate the number change back to monthly animateNumber(price, currentValue, originalMonthlyValue); }); + + // Update payment links to monthly + document.querySelectorAll('.payment-button').forEach(button => { + const monthlyLink = button.getAttribute('data-monthly-link'); + if (monthlyLink) { + button.href = monthlyLink; + } + }); } }); }); diff --git a/templates/settings/tabs/pricing.html b/templates/settings/tabs/pricing.html index f5afabf..e26bed2 100644 --- a/templates/settings/tabs/pricing.html +++ b/templates/settings/tabs/pricing.html @@ -53,6 +53,35 @@ + + {% if plan.monthly_stripe_link or plan.annual_stripe_link %} +
+ Payment Links: +
+ {% if plan.monthly_stripe_link %} +
+ + Monthly: + + Stripe Payment Link + + +
+ {% endif %} + {% if plan.annual_stripe_link %} +
+ + Annual: + + Stripe Payment Link + + +
+ {% endif %} +
+
+ {% endif %} +
Features:
    @@ -220,11 +249,24 @@ value="Get Started">
+ + + +
- - + + + Stripe payment link for monthly billing +
+
+
+
+ + + Stripe payment link for annual billing
@@ -367,10 +409,24 @@ + + + +
- - + + + Stripe payment link for monthly billing +
+
+
+
+ + + Stripe payment link for annual billing