Merge commit '129d3b793d97ea32e6f42b4ca1f33bb0860157e0' into production

This commit is contained in:
Rob 2015-12-16 16:46:00 -08:00
commit 846fb987d2
149 changed files with 10771 additions and 10075 deletions
app
scripts
server
server_setup.coffee
spec/fixtures

Binary file not shown.

After

(image error) Size: 979 B

Binary file not shown.

After

(image error) Size: 449 B

Binary file not shown.

After

(image error) Size: 119 KiB

Binary file not shown.

After

(image error) Size: 122 KiB

Binary file not shown.

After

(image error) Size: 116 KiB

Binary file not shown.

After

(image error) Size: 120 KiB

Binary file not shown.

After

(image error) Size: 144 KiB

Binary file not shown.

After

(image error) Size: 86 KiB

Binary file not shown.

After

(image error) Size: 123 KiB

Binary file not shown.

After

(image error) Size: 438 B

Binary file not shown.

After

(image error) Size: 663 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 75 KiB

Binary file not shown.

After

(image error) Size: 37 KiB

Binary file not shown.

After

(image error) Size: 471 B

View file

@ -0,0 +1,8 @@
CocoCollection = require './CocoCollection'
Product = require 'models/Product'
module.exports = class Products extends CocoCollection
model: Product
url: '/db/products'
getByName: (name) -> @findWhere { name: name }

View file

@ -24,7 +24,6 @@ module.exports = class CocoRouter extends Backbone.Router
'account/profile': go('EmployersView') # Show the not-recruiting-now screen
'account/payments': go('account/PaymentsView')
'account/subscription': go('account/SubscriptionView')
'account/subscription/sale': go('account/SubscriptionSaleView')
'account/invoices': go('account/InvoicesView')
'account/prepaid': go('account/PrepaidView')
@ -126,6 +125,8 @@ module.exports = class CocoRouter extends Backbone.Router
'preview': go('HomeView')
'schools': go('SalesView')
'teachers': go('TeachersView')
'teachers/freetrial': go('TeachersFreeTrialView')

View file

@ -244,7 +244,7 @@ module.exports.getCoursePraise = getCoursePraise = ->
]
praise[_.random(0, praise.length - 1)]
module.exports.getPrepaidCodeAmount = getPrepaidCodeAmount = (price=999, users=0, months=0) ->
module.exports.getPrepaidCodeAmount = getPrepaidCodeAmount = (price=0, users=0, months=0) ->
return 0 unless users > 0 and months > 0
total = price * users * months
total

View file

@ -59,6 +59,7 @@ module.exports = class Simulator extends CocoClass
success: (taskData) =>
return if @destroyed
unless taskData
@retryDelayInSeconds = 10
@trigger 'statusUpdate', "No games to simulate. Trying another game in #{@retryDelayInSeconds} seconds."
@simulateAnotherTaskAfterDelay()
return

View file

@ -4,7 +4,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
no_ie: "CodeCombat no funciona en Internet Explorer 8 o versions anteriors. Perdó!" # Warning that only shows up in IE8 and older
no_mobile: "CodeCombat no ha estat dissenyat per dispositius mòbils i per tant no funcionarà!" # Warning that shows up on mobile devices
play: "Jugar" # The big play button that opens up the campaign view.
# play_campaign_version: "Play Campaign Version" # Shows up under big play button if you only play /courses
play_campaign_version: "Juga a la versió campanya" # Shows up under big play button if you only play /courses
old_browser: "Uh oh, el teu navegador és massa antic per fer funcionar CodeCombat. Perdó!" # Warning that shows up on really old Firefox/Chrome/Safari
old_browser_suffix: "Pots probar-ho igualment, però el més segur és que no funcioni."
ipad_browser: "Males notícies: CodeCombat no funciona en el navegador d'iPad. Bones notícies: la nostre aplicació d'iPad està esperant l'aprovació d'Apple."
@ -12,17 +12,17 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
for_beginners: "Per a principiants"
multiplayer: "Multijugador" # Not currently shown on home page
for_developers: "Per a Desenvolupadors" # Not currently shown on home page.
or_ipad: "O descarrèga'l per iPad"
# hoc_class_code: "I Have a Class Code"
# hoc_enter: "Enter"
# hoc_title: "Hour of Code?"
or_ipad: "O descarrega'l per iPad"
hoc_class_code: "Tinc un codi de classe"
hoc_enter: "Introduir"
hoc_title: "Hora del codi?"
nav:
play: "Nivells" # The top nav bar entry where players choose which levels to play
community: "Comunitat"
# courses: "Courses"
courses: "Cursos"
editor: "Editor"
blog: "Blog"
blog: "Bloc"
forum: "Fòrum"
account: "Compte"
profile: "Perfil"
@ -35,30 +35,30 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
about: "Sobre Nosaltres"
contact: "Contacta"
twitter_follow: "Segueix-nos"
teachers: "Profesors"
# careers: "Careers"
teachers: "Professors"
careers: "Professions"
modal:
close: "Tancar"
okay: "Okey"
okay: "D'acord"
not_found:
page_not_found: "Pagina no trobada"
page_not_found: "Pàgina no trobada"
diplomat_suggestion:
title: "Ajuda a traduir CodeCombat!" # This shows up when a player switches to a non-English language using the language selector.
sub_heading: "Neccesitem les teves habilitats lingüístiques."
pitch_body: "Hem desenvolupat CodeCombat en anglès, peró tenim jugadors per tot el món. Molts d'ells volen jugar en Català, però no parlen anglès, per tant si pots parlar ambdós llengües, siusplau considereu iniciar sesió per a ser Diplomàtic i ajudar a traduir la web de CodeCombat i tots els seus nivell en Català."
pitch_body: "Hem desenvolupat CodeCombat en anglès, però tenim jugadors per tot el món. Molts d'ells volen jugar en Català, però no parlen anglès, per tant si pots parlar ambdues llengües, siusplau considereu iniciar sessió per a ser Diplomàtic i ajudar a traduir la web de CodeCombat i tots els seus nivells en Català."
missing_translations: "Fins que puguem traduir-ho tot en català, ho veuràs en anglès quant no estigui en català."
learn_more: "Aprèn més sobre ser un diplomàtic"
subscribe_as_diplomat: "Subscriu-te com a diplomàtic"
play:
play_as: "Jugar com" # Ladder page
# compete: "Compete!" # Course details page
spectate: "Spectate" # Ladder page
players: "jugadors" # Hover over a level on /play
hours_played: "hores de joc" # Hover over a level on /play
compete: "Competir!" # Course details page
spectate: "Espectador" # Ladder page
players: "Jugadors" # Hover over a level on /play
hours_played: "Hores jugades" # Hover over a level on /play
items: "Objectes" # Tooltip on item shop button from /play
unlock: "Desbloquejar" # For purchasing items and heroes
confirm: "Confirmar"
@ -71,31 +71,31 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
achievements: "Triomfs" # Tooltip on achievement list button from /play
account: "Compte" # Tooltip on account button from /play
settings: "Configuració" # Tooltip on settings button from /play
# poll: "Poll" # Tooltip on poll button from /play
poll: "Enquesta" # Tooltip on poll button from /play
next: "Següent" # Go from choose hero to choose inventory before playing a level
change_hero: "Canviar heroi" # Go back from choose inventory to choose hero
buy_gems: "Comprar Gemes"
subscription_required: "Subscripció necessària"
anonymous: "Jugador anònim"
level_difficulty: "Dificultat: "
# play_classroom_version: "Play Classroom Version" # Choose a level in campaign version that you also can play in one of your courses
play_classroom_version: "Juga a la versió de classe" # Choose a level in campaign version that you also can play in one of your courses
campaign_beginner: "Campanya del principiant"
awaiting_levels_adventurer_prefix: "Fem cinc nivells per setmana" # {change}
awaiting_levels_adventurer: "Inicia sessió com aventurer"
awaiting_levels_adventurer_suffix: "sigues el primer en jugar els nous nivells"
awaiting_levels_adventurer: "Inicia sessió com a aventurer"
awaiting_levels_adventurer_suffix: "Sigues el primer en jugar els nous nivells"
adjust_volume: "Ajustar volum"
campaign_multiplayer: "Arenes Multijugador"
campaign_multiplayer_description: "... on programes cara a cara contra altres jugadors."
# campaign_old_multiplayer: "(Deprecated) Old Multiplayer Arenas"
# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas."
campaign_old_multiplayer: "(Obsolet) Arenes multijugador antigues"
campaign_old_multiplayer_description: "Relíquies d'una era més civilitzada. No hi ha simulacions per aquestes arenes multijugador antigues."
share_progress_modal:
blurb: "Estàs progressant molt! Digues a algú quan n'has après amb CodeCombat." # {change}
email_invalid: "Correu electrònic invalid."
email_invalid: "Correu electrònic invàlid."
form_blurb: "Escriu els seus emails a sota i els hi ensenyarem!"
form_label: "Correu electrònic"
placeholder: "adreça de correu electrònic"
title: "Excelent feina, Aprenent"
title: "Excel·lent feina, aprenent"
login:
sign_up: "Crear un compte"
@ -115,12 +115,12 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
creating: "Creant Compte..."
sign_up: "Registrar-se"
log_in: "Iniciar sessió amb la teva contrasenya"
social_signup: "O, pots iniciar sessió desde Facebook o G+:"
social_signup: "O pots iniciar sessió desde Facebook o G+:"
required: "Neccesites iniciar sessió abans ."
login_switch: "Ja tens un compte?"
# school_name: "School Name and City"
# optional: "optional"
# school_name_placeholder: "Example High School, Springfield, IL"
school_name: "Nom de l'institut i ciutat"
optional: "opcional"
school_name_placeholder: "Exemple: IES Mossèn Alcover, Manacor"
recover:
recover_account_title: "Recuperar Compte"
@ -156,7 +156,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
unwatch: "Amaga"
submit_patch: "Enviar pegat"
submit_changes: "Puja canvis"
# save_changes: "Save Changes"
save_changes: "Guarda canvis"
general:
and: "i"
@ -168,15 +168,15 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
accepted: "Acceptat"
rejected: "Rebutjat"
withdrawn: "Retirat"
# accept: "Accept"
# reject: "Reject"
# withdraw: "Withdraw"
accept: "Accepta"
reject: "Refusa"
withdraw: "Retira"
submitter: "Remitent"
submitted: "Presentat"
commit_msg: "nissatge de Commir"
commit_msg: "Enviar missatge"
version_history: "Historial de versions"
version_history_for: "Historial de versions per: "
select_changes: "Seleciona dos canvis sota per veure les diferencies."
select_changes: "Selecciona dos canvis de sota per veure les diferències."
undo_prefix: "Desfer"
undo_shortcut: "(Ctrl+Z)"
redo_prefix: "Refés"
@ -186,8 +186,8 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
results: "Resultats"
description: "Descripció"
or: "o"
subject: "Subjecte"
email: "Email"
subject: "Tema"
email: "Correu electrònic"
password: "Contrasenya"
message: "Missatge"
code: "Codi"
@ -205,7 +205,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
player: "Jugador"
player_level: "Nivell" # Like player level 5, not like level: Dungeons of Kithgard
warrior: "Guerrer"
ranger: "Ranger"
ranger: "Guardabosc"
wizard: "Mag"
units:
@ -225,22 +225,22 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
years: "anys"
play_level:
# completed_level: "Completed Level:"
# course: "Course:"
completed_level: "Nivell completat:"
course: "Curs:"
done: "Fet"
# next_level: "Next Level:"
# next_game: "Next game"
# show_menu: "Show game menu"
next_level: "Següent nivell:"
next_game: "Següent joc"
show_menu: "Mostrar menú del joc"
home: "Inici" # Not used any more, will be removed soon.
level: "Nivell" # Like "Level: Dungeons of Kithgard"
skip: "Ometre"
game_menu: "Menu de joc"
game_menu: "Menú de joc"
guide: "Guia"
restart: "Recomençar"
restart: "Reiniciar"
goals: "Objectius"
goal: "Objectiu"
running: "Executant..."
success: "Exit!"
success: "Èxit!"
incomplete: "Incomplet"
timed_out: "S'ha acabat el temps"
failing: "Fallant"
@ -250,88 +250,88 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
control_bar_join_game: "Entrar al joc"
reload: "Recarregar"
reload_title: "Recarregar tot el codi?"
reload_really: "Estàs segur que vos recarregar aquest nivell al principi?"
reload_really: "Estàs segur que vols recarregar aquest nivell des del principi?"
reload_confirm: "Recarregar tot"
victory: "Victòria"
victory_title_prefix: ""
victory_title_suffix: " Complet"
victory_sign_up: "Inicia sessió per a desar el progressos"
victory_sign_up_poke: "Vols guardar el teu codi? Crea un compte gratuit!"
victory_sign_up_poke: "Vols guardar el teu codi? Crea un compte gratuït!"
victory_rate_the_level: "Valora el nivell: " # {change}
victory_return_to_ladder: "Retorna a les Escales"
victory_saving_progress: "Desa progrés"
victory_go_home: "Tornar a l'inici"
victory_review: "Diguens més!"
# victory_review_placeholder: "How was the level?"
victory_review: "Explica'ns més!"
victory_review_placeholder: "Com ha anat el nivell?"
victory_hour_of_code_done: "Has acabat?"
victory_hour_of_code_done_yes: "Sí, He acabat amb la meva Hora de Codi™!"
victory_hour_of_code_done_yes: "Sí, he acabat amb la meva Hora del Codi™!"
victory_experience_gained: "XP Guanyada"
victory_gems_gained: "Gemmes guanyades"
# victory_new_item: "New Item"
# victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks."
# victory_become_a_viking: "Become a Viking"
victory_new_item: "Objecte nou"
victory_viking_code_school: "Ostres! Aquest nivell era un nivell difícil de superar! Si no ets un programador, ho hauries de ser. Acabes d'aconseguir una acceptació per la via ràpida a l'Escola de Programació Vikinga, on pot millorar les teves habilitats fins al següent nivell i esdevenir un programador de webs professional en 14 setmanes."
victory_become_a_viking: "Converteix-te en un víking"
guide_title: "Guia"
tome_cast_button_run: "Executar"
tome_cast_button_running: "Executant"
tome_cast_button_ran: "Executat"
tome_submit_button: "Envia"
tome_reload_method: "Recarrega el codi original code per aquest metòde" # Title text for individual method reload button.
tome_reload_method: "Recarrega el codi original per aquest metòde" # Title text for individual method reload button.
tome_select_method: "Selecciona un mètode"
tome_see_all_methods: "Veure tots els mètodes que pots editar" # Title text for method list selector (shown when there are multiple programmable methods).
tome_select_a_thang: "Selecciona Algú Per"
tome_select_a_thang: "Selecciona algú Per"
tome_available_spells: "Encanteris disponibles"
tome_your_skills: "Les teves habilitats"
tome_current_method: "Mètode actual"
code_saved: "Codi Guardat"
skip_tutorial: "Passar (esc)"
skip_tutorial: "Ometre (esc)"
keyboard_shortcuts: "Dreceres del teclat"
loading_ready: "Preparat!"
loading_start: "Comença el nivell"
problem_alert_title: "Arregla el Teu Codi"
time_current: "Ara:"
time_total: "Maxim:"
time_total: "Màxim:"
time_goto: "Ves a:"
# non_user_code_problem_title: "Unable to Load Level"
# infinite_loop_title: "Infinite Loop Detected"
# infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know."
# check_dev_console: "You can also open the developer console to see what might be going wrong."
# check_dev_console_link: "(instructions)"
non_user_code_problem_title: "Impossible carregar el nivell"
infinite_loop_title: "Detectat un bucle infinit"
infinite_loop_description: "El codi inicial mai acaba d'executar-se. Probablement sigui molt lent o tingui un bucle infinit. O pot ser un error. Pots provar de tornar a executar el codi o reiniciar-lo al seu estat original. Si no es soluciona, si us plau, fes-nos-ho saber."
check_dev_console: "També pots obrir la consola de desenvolupament per veure què surt malament."
check_dev_console_link: "(instruccions)"
infinite_loop_try_again: "Tornar a intentar"
infinite_loop_reset_level: "Reiniciar nivell"
infinite_loop_comment_out: "Descommenta el Meu Codi"
infinite_loop_comment_out: "Treu els comentaris del meu codi"
tip_toggle_play: "Canvia entre reproduir/pausa amb Ctrl+P"
tip_scrub_shortcut: "Ctrl+[ i Ctrl+] per rebobinar i avançar" # {change}
# tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info."
tip_guide_exists: "Clica a la guia dins el menú del joc (a la part superior de la pàgina) per informació útil."
tip_open_source: "CodeCombat és 100% codi lliure!"
# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!"
tip_tell_friends: "Gaudint de CodeCombat? Explica'ls-ho als teus amics!"
tip_beta_launch: "CodeCombat va llançar la seva beta l'octubre de 2013."
tip_think_solution: "Pensa en la solució,no en el problema."
tip_think_solution: "Pensa en la solució, no en el problema."
tip_theory_practice: "En teoria no hi ha diferència entre la teoria i la pràctica. Però a la pràctica si que n'hi ha. - Yogi Berra"
tip_error_free: "Només hi ha dues maneres d'escriure programes sense errors; la tercera és la única que funciona. - Alan Perlis"
tip_debugging_program: "Si debuguejar és el procés d'eliminar errors, llavors programar és el procés de posar-los. - Edsger W. Dijkstra"
# tip_forums: "Head over to the forums and tell us what you think!"
tip_baby_coders: "En el futur fins i tot els bebés podran ser Artximags."
# tip_morale_improves: "Loading will continue until morale improves."
# tip_all_species: "We believe in equal opportunities to learn programming for all species."
# tip_reticulating: "Reticulating spines."
# tip_harry: "Yer a Wizard, "
# tip_great_responsibility: "With great coding skill comes great debug responsibility."
# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep."
tip_binary: "Hi ha 10 tipus de persones al mon, les que saben programar en binari i les que no"
# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda"
# tip_no_try: "Do. Or do not. There is no try. - Yoda"
# tip_patience: "Patience you must have, young Padawan. - Yoda"
# tip_documented_bug: "A documented bug is not a bug; it is a feature."
# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela"
# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds"
# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay"
# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem."
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
# tip_superpower: "Coding is the closest thing we have to a superpower."
# tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds"
tip_debugging_program: "Si depurar és el procés per eliminar errors, llavors programar és el procés de posar-los. - Edsger W. Dijkstra"
tip_forums: "Passa pels fòrums i digues el que penses!"
tip_baby_coders: "En el futur fins i tot els nadons podran ser mags."
tip_morale_improves: "La càrrega continuarà fins que la moral millori."
tip_all_species: "Creiem en la igualtat d'oportunitats per aprendre a programar per a totes les espècies."
tip_reticulating: "Reticulant punxes."
tip_harry: "Ets un bruixot, "
tip_great_responsibility: "Un gran coneixement del codi comporta una gran responsabilitat per a depurar-lo."
tip_munchkin: "Si no menges les teves verdures, un munchkin vindrà mentre dormis."
tip_binary: "Hi ha 10 tipus de persones al món, les que saben programar en binari i les que no"
tip_commitment_yoda: "Un programador ha de tenir un compromís profund, una ment seriosa. ~ Yoda"
tip_no_try: "Fes-ho. O no ho facis. Però no ho intentis. - Yoda"
tip_patience: "Pacient has de ser, jove Padawan. - Yoda"
tip_documented_bug: "Un error documentat no és un error; és un atractiu."
tip_impossible: "Sempre sembla impossible fins que es fa. - Nelson Mandela"
tip_talk_is_cheap: "Parlar és barat. Mostra'm el codi. - Linus Torvalds"
tip_first_language: "La cosa més desastrosa que aprendràs mai és el teu primer llenguatge de programació. - Alan Kay"
tip_hardware_problem: "P: Quants programadors són necessaris per canviar una bombeta? R: Cap, és un problema de hardware."
tip_hofstadters_law: "Llei de Hofstadter: Sempre et portarà més feina del que esperaves, fins i tot tenint en compte la llei de Hofstadter."
tip_premature_optimization: "La optimització prematura és l'arrel de la maldat. - Donald Knuth"
tip_brute_force: "Quan dubtis, usa força bruta. - Ken Thompson"
tip_extrapolation: "Hi ha dos tipus de persones: aquells que poden extrapolar des de dades incompletes..."
tip_superpower: "Programar és el que més s'aproxima a un super poder."
tip_control_destiny: "En un codi obert real tens el dret a controlar el teu propi destí. - Linus Torvalds"
# tip_no_code: "No code is faster than no code."
# tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries"
# tip_reusable_software: "Before software can be reusable it first has to be usable."

View file

@ -4,7 +4,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
no_ie: "Το CodeCombat δεν είναι συμβατό με το Internet Explorer 9 ή κάποια παλαιότερη έκδοση. Συγνώμη!" # Warning that only shows up in IE8 and older
no_mobile: "Το CodeCombat δεν σχεδιάστηκε για κινητά και μπορεί να μην δουλεύει!" # Warning that shows up on mobile devices
play: "Παίξε" # The big play button that opens up the campaign view.
# play_campaign_version: "Play Campaign Version" # Shows up under big play button if you only play /courses
play_campaign_version: "Παίξε σε Μορφή Εκστρατείας" # Shows up under big play button if you only play /courses
old_browser: "Ωχ, ο περιηγητής σας είναι πολύ παλιός για να τρέξει το CodeCombat. Συγνώμη!" # Warning that shows up on really old Firefox/Chrome/Safari
old_browser_suffix: "Μπορείτε να δοκιμάσετε, αλλά πιθανότατα να μην λειτουργήσει."
ipad_browser: "Δυσάρεστα νέα: Το CodeCombat δεν τρέχει στο iPad μέσω περιηγητή. Ευχάριστα νέα: αναμένουμε την έγκριση της Apple για την εφαρμογή μας για iPad."
@ -78,7 +78,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
subscription_required: "Απαιτείται εγγραφή"
anonymous: "Ανώνυμοι Παίκτες"
level_difficulty: "Δυσκολία: "
# play_classroom_version: "Play Classroom Version" # Choose a level in campaign version that you also can play in one of your courses
play_classroom_version: "Παίξε σε Μορφή Μαθημάτων" # Choose a level in campaign version that you also can play in one of your courses
campaign_beginner: "Εκστρατεία για Αρχάριους"
awaiting_levels_adventurer_prefix: "Έχουμε νέα επίπεδα κάθε εβδομάδα."
awaiting_levels_adventurer: "Εγγραφή ως Adventurer"
@ -118,9 +118,9 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
social_signup: "Ή, μπορείς να συνδεθείς μέσω Facebook ή G+:"
required: "Θα πρέπει να συνδεθείτε πριν πάτε προς τα εκεί."
login_switch: "Έχεις ήδη λογαριασμό;"
# school_name: "School Name and City"
# optional: "optional"
# school_name_placeholder: "Example High School, Springfield, IL"
school_name: "Ονομα Σχολείου και Πόλη"
optional: "Προαιρετικό"
school_name_placeholder: "Παράδειγμα ΕΠΑΛ Καλύμνου, Κάλυμνος"
recover:
recover_account_title: "Ανάκτηση λογαριασμού"
@ -249,7 +249,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
control_bar_multiplayer: "πολλαπλών παικτών"
control_bar_join_game: "Παίξε κι εσύ"
reload: "Επαναφόρτωση"
reload_title: "Ανανέωση όλου του κωδικά;"
reload_title: "Ανανέωση όλου του κώδικα;"
reload_really: "Είσαι σίγουρος ότι θέλεις να φορτώσεις αυτό το επίπεδο από την αρχή;"
reload_confirm: "Ανανέωση όλων"
victory: "Νίκη"
@ -855,7 +855,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
started_2: "Ξεκίνησε"
not_started_2: "Δεν ξεκίνησε"
view_solution: "Πάτα για να δεις τη λύση."
# view_attempt: "Click to view attempt."
view_attempt: "Κλικ για να δείς την προσπάθεια."
latest_achievement: "Τελευταίο Επίτευγμα"
playtime: "Χρόνος παιχνιδιού"
last_played: "Έπαιξε τελευταία φορά"
@ -877,7 +877,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
courses:
course: "Μάθημα"
courses: "μαθήματα"
# create_new_class: "Create New Class"
create_new_class: "Δημιουργια Νεας Ταξης"
not_enrolled: "Δεν έχετε εγγραφεί στο μάθημα αυτό."
visit_pref: "Παρακαλώ επισκεφτείτε τη "
visit_suf: "σελίδα για να εγγραφείτε."
@ -952,149 +952,148 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
topics: "Θέματα"
hours_content: "Ώρες περιεχομένου:"
get_free: "Πάρε ΔΩΡΕΑΝ μάθημα"
# enroll_paid: "Enroll Students in Paid Courses"
# you_have1: "You have"
# you_have2: "unused paid enrollments"
# use_one: "Use 1 paid enrollment for"
# use_multiple: "Use paid enrollments for the following students:"
# already_enrolled: "already enrolled"
# licenses_remaining: "licenses remaining:"
# insufficient_enrollments: "insufficient paid enrollments"
# enroll_students: "Enroll Students"
# get_enrollments: "Get More Enrollments"
# change_language: "Change Course Language"
# keep_using: "Keep Using"
# switch_to: "Switch To"
# greetings: "Greetings!"
# learn_p: "Learn Python"
# learn_j: "Learn JavaScript"
# language_cannot_change: "Language cannot be changed once students join a class."
# back_classrooms: "Back to my classrooms"
# back_courses: "Back to my courses"
# edit_details: "Edit class details"
# enrolled_courses: "enrolled in paid courses:"
# purchase_enrollments: "Purchase Enrollments"
# remove_student: "remove student"
# assign: "Assign"
# to_assign: "to assign paid courses."
# teacher: "Teacher"
# complete: "Complete"
# none: "None"
# save: "Save"
# play_campaign_title: "Play the Campaign"
# play_campaign_description: "Youre ready to take the next step! Explore hundreds of challenging levels, learn advanced programming skills, and compete in multiplayer arenas!"
# create_account_title: "Create an Account"
# create_account_description: "Sign up for a FREE CodeCombat account and gain access to more levels, more programming skills, and more fun!"
# preview_campaign_title: "Preview Campaign"
# preview_campaign_description: "Take a sneak peek at all that CodeCombat has to offer before signing up for your FREE account."
# arena: "Arena"
# arena_soon_title: "Arena Coming Soon"
# arena_soon_description: "We are working on a multiplayer arena for classrooms at the end of"
# not_enrolled1: "Not enrolled"
# not_enrolled2: "Ask your teacher to enroll you in the next course."
# next_course: "Next Course"
# coming_soon1: "Coming soon"
# coming_soon2: "We are hard at work making more courses for you!"
# available_levels: "Available Levels"
# welcome_to_courses: "Adventurers, welcome to Courses!"
# ready_to_play: "Ready to play?"
# start_new_game: "Start New Game"
# play_now_learn_header: "Play now to learn"
# play_now_learn_1: "basic syntax to control your character"
# play_now_learn_2: "while loops to solve pesky puzzles"
# play_now_learn_3: "strings & variables to customize actions"
# play_now_learn_4: "how to defeat an ogre (important life skills!)"
# welcome_to_page: "Welcome to your Courses page!"
# completed_hoc: "Amazing! You've completed the Hour of Code course!"
# ready_for_more_header: "Ready for more? Play the campaign mode!"
# ready_for_more_1: "Use gems to unlock new items!"
# ready_for_more_2: "Play through brand new worlds and challenges"
# ready_for_more_3: "Learn even more programming!"
# saved_games: "Saved Games"
# hoc: "Hour of Code"
# my_classes: "My Classes"
# class_added: "Class successfully added!"
# view_class: "view class"
# view_levels: "view levels"
# join_class: "Join A Class"
# ask_teacher_for_code: "Ask your teacher if you have a CodeCombat class code! If so, enter it below:"
# enter_c_code: "<Enter Class Code>"
# join: "Join"
# joining: "Joining class"
# course_complete: "Course Complete"
# play_arena: "Play Arena"
# start: "Start"
# last_level: "Last Level"
# welcome_to_hoc: "Adventurers, welcome to our Hour of Code!"
# logged_in_as: "Logged in as:"
# not_you: "Not you?"
# welcome_back: "Hi adventurer, welcome back!"
# continue_playing: "Continue Playing"
# more_options: "More options:"
# option1_header: "Option 1: Invite students via email"
# option1_body: "Students will automatically be sent an invitation to join this class, and will need to create an account with a username and password."
# option2_header: "Option 2: Send URL to your students"
# option2_body: "Students will be asked to enter an email address, username and password to create an account."
# option3_header: "Option 3: Direct students to codecombat.com/courses"
# option3_body: "Give students the following passcode to enter along with an email address, username and password when they create an account."
# thank_you_pref: "Thank you for your purchase! You can now assign"
# thank_you_suff: "more students to paid courses."
# return_to_class: "Return to classroom"
# return_to_course_man: "Return to course management."
# students_not_enrolled: "students not enrolled"
# total_all_classes: "Total Across All Classes"
# how_many_enrollments: "How many additional paid enrollments do you need?"
# each_student_access: "Each student in a class will get access to Courses 2-4 once they are enrolled in paid courses. You may assign each course to each student individually."
# purchase_now: "Purchase Now"
# enrollments: "enrollments"
# remove_student1: "Remove Student"
# are_you_sure: "Are you sure you want to remove this student from this class?"
# remove_description1: "Student will lose access to this classroom and assigned classes. Progress and gameplay is NOT lost, and the student can be added back to the classroom at any time."
# remove_description2: "The activated paid license will not be returned."
# keep_student: "Keep Student"
# removing_user: "Removing user"
# to_join_ask: "To join a class, ask your teacher for an unlock code."
# join_this_class: "Join Class"
# enter_here: "<enter unlock code here>"
# successfully_joined: "Successfully joined"
# click_to_start: "Click here to start taking"
# my_courses: "My Courses"
# classroom: "Classroom"
# use_school_email: "use your school email if you have one"
# unique_name: "a unique name no one has chosen"
# pick_something: "pick something you can remember"
# class_code: "Class Code"
# optional_ask: "optional - ask your teacher to give you one!"
# optional_school: "optional - what school do you go to?"
# start_playing: "Start Playing"
# skip_this: "Skip this, I'll create an account later!"
# welcome: "Welcome"
# getting_started: "Getting Started with Courses"
# download_getting_started: "Download Getting Started Guide [PDF]"
# getting_started_1: "Create a new class by clicking the green 'Create New Class' button below."
# getting_started_2: "Once you've created a class, click the blue 'Add Students' button."
# getting_started_3: "You'll see student's progress below as they sign up and join your class."
# additional_resources: "Additional Resources"
# additional_resources_1_pref: "Download/print our"
# additional_resources_1_mid: "Course 1 Teacher's Guide"
# additional_resources_1_suff: "explanations and solutions to each level."
# additional_resources_2_pref: "Complete our"
# additional_resources_2_suff: "to get 15 more hours of content for FREE for 2 months."
# additional_resources_3_pref: "Visit our"
# additional_resources_3_mid: "Teacher Forums"
# additional_resources_3_suff: "to connect to fellow educators who are using CodeCombat."
# your_classes: "Your Classes"
# no_classes: "No classes yet!"
# create_new_class1: "create new class"
# available_courses: "Available Courses"
# unused_enrollments: "Unused enrollments available:"
# students_access: "All students get access to Introduction to Computer Science for free. One enrollment per student is required to assign them to paid CodeCombat courses. A single student does not need multiple enrollments to access all paid courses."
# active_courses: "active courses"
# no_students: "No students yet!"
# add_students1: "add students"
# view_edit: "view/edit"
# students_enrolled: "students enrolled"
# length: "Length:"
enroll_paid: "Γράψε μαθητές σε μαθήματα με πληρωμή"
you_have1: "Έχεις"
you_have2: "αχρησιμοποιήτες πληρωμένες συνδρομές"
use_one: "Χρησιμοποιήσε 1 πληρωμένη συνδρομή για"
use_multiple: "Χρησιμοποιήσε τις πληρωμένη συνδρομές για τους παρακάτω μαθητές:"
already_enrolled: "Ήδη εγγεγραμένος"
licenses_remaining: "Συνδρομές που απομένουν:"
insufficient_enrollments: "Δεν αρκούν οι πληρωμένες συνδρομές"
enroll_students: " Γράψε Μαθητές"
get_enrollments: "Ζήτα και αλλές εγγραφές"
change_language: "Αλλαγή Γλώσσας Μαθήματος"
keep_using: "Συνέχισε να χρησιμοποιείς"
switch_to: "Αλλαγή σε"
greetings: "Καλως ΄Ηρθες!"
learn_p: "Μάθε Python"
learn_j: "Μάθε JavaScript"
language_cannot_change: "Η γλώσσα δεν μπορεί να αλλάξει από τη στιγμή που οι μαθητές θα συνδεθούν με την τάξη."
back_classrooms: "Πίσω στις τάξεις μου"
back_courses: "Πίσω στα μαθήματα μου"
edit_details: "Επεξεργασία Ρυθμίσεων Τάξης"
enrolled_courses: "Εγγεγραμμένοι σε μαθήματα με πληρωμή:"
purchase_enrollments: "Αγόρασε Συνδρομές επί πληρωμή"
remove_student: "Διαγραφή Μαθητή"
assign: "Ανάθεση"
to_assign: "Ανάθεση μαθημάτων επί πληρωμή."
teacher: "Δάσκαλος"
complete: "Ολοκληρωμένο"
none: "Κανένα"
save: "Αποθήκευση"
play_campaign_title: "Παίξε την Εκστρατεία"
play_campaign_description: "Είσαι έτοιμος να κάνει το επόμενο βήμα! Εξερευνήστε εκατοντάδες επίπεδα γεμάτα με νεές προκλήσεις, αποκτώνγτας προηγμένες γνώσεις προγραμματισμού, και ανταγωνιστείτε σε αρένες με άλλους παίκτες!"
create_account_title: "Δημιουργία Λογαριασμού"
create_account_description: "Εγγραφείτε για ένα Δωρέαν CodeCombat λογαριασμό και κερδίστε πρόσβαση σε περισσότερα επίπεδα και προγραμματιστικές τεχνικές και φυσικά περισσότερη διασκέδαση!"
preview_campaign_title: "Προεπισκόπηση Εκστρατείας"
preview_campaign_description: "Ρίξτε μια κλεφτή ματιά σε όλα όσα το CodeCombat έχει να προσφέρει πριν από την εγγραφή σας για το ΔΩΡΕΑΝ λογαριασμό."
arena: "Αρενα"
arena_soon_title: "Σύντομα Έρχεται Αρένα"
arena_soon_description: "Κατασκευάζουμε μια αρένα πολλών παικτών για τάξεις στο τέλος του"
not_enrolled1: "Όχι εγγεγραμμένος"
not_enrolled2: "Ζήτα από το δάσκαλο σου να σε εγγράψει στο επόμενο μάθημα."
next_course: "Επόμενο Μάθημα"
coming_soon1: "Έρχεται Σύντομα"
coming_soon2: "Εργαζόμαστε σκληρά, φτιάχνοντας νέα μαθήματα για εσάς!"
available_levels: "Διαθέσιμα Επίπεδα"
welcome_to_courses: " Εξερευνητές καλωσήρθατε στις Σειρές Μαθημάτων!"
ready_to_play: "Έτοιμος να παίξεις?"
start_new_game: " Ξεκίνα Νέο Παιχνίδι"
play_now_learn_header: "Παίξε για να μάθεις"
play_now_learn_1: "Βασικό συντακτικό για τον έλεγχο του ήρωα σου."
play_now_learn_2: "Επαναληπτικοί Βρόχοι While για να λύνεις βαρετούς γρίφους"
play_now_learn_3: "συμβολοσειρές και μεταβλητές για να τροποποιείς εύκολα τις ενέργειες σου"
play_now_learn_4: " Πως να νικησεις ένα Ogre(απαραίτητο μάθημα επιβίωσης!)"
welcome_to_page: "Καλως ήρθες στη σελίδα των μαθημάτων σου!"
completed_hoc: "Καταπληκτικό! Ολοκλήρωσες την ώρα του κώδικα!"
ready_for_more_header: "Έτοιμος για περισσότερα? Παίξε στις Εκστρατείες!"
ready_for_more_1: "Χρησιμοποιήσε τα πετράδια για να ξεκλειδώσεις νέα αντικείμενα!"
ready_for_more_2: "Παίξε σενέους κόσμους κυνηγώντας καινούριες προκλήσεις"
ready_for_more_3: "Μάθε περισσότερα πάνω στο Προγραμματισμό!"
saved_games: "Αποθηκευμένα Παιχνίδια"
hoc: "Ώρα του Κώδικα"
my_classes: "Οι Τάξεις μου"
class_added: "Τάξη Προστέθηκε!"
view_class: "Ελέγχος Τάξης"
view_levels: "Ελέγχος Επίπεδων"
join_class: "Σύνδεση με Τάξη"
ask_teacher_for_code: "Ζήτα από το δάσκαλο σου, εάν έχει ένα κωδικό τάξης για το CodeCombat! Εάν έχει, πληκτρολόγησε το παρακάτω:"
enter_c_code: "<Πληκτρολόγησε Κωδικό Τάξης>"
join: "Σύνδεση"
joining: "Σύνδεση με Τάξη"
course_complete: "Μάθημα Ολοκληρωμένο"
play_arena: "Παίξε στην Αρένα"
start: "Εκκίνηση"
last_level: "Προηγουμένο Επίπεδο"
welcome_to_hoc: "Εξερευνητές, καλωσήρθατε στη δικιά μας Ώρα του Κώδικα!"
logged_in_as: "Υπογράφεις ως:"
not_you: "Δεν είσαι εσύ?"
welcome_back: "Γειά σου εξερευνητή καλωσήρθες πίσω!"
continue_playing: "Συνέχιση Παιχνιδιού"
more_options: "Περισσότερες Επιλογές:"
option1_header: "Επιλογή 1: Πρόσκληση στους μαθητές μέσω email"
option1_body: "Οι μαθητές θα λάβουν αυτομάτα μια πρόσκληση να συνδεθούν με την τάξηε, θα χρειαστεί να δημιουργήσουν ένα λογαριασμό με όνομα χρήστη και κωδικό πρόσβασης."
option2_header: "Επιλογή 2: Στείλε το URL στους μαθητές σου"
option2_body: "Από τους μαθητές θα ζητηθεί να εισάγουν μια διεύθυνση email, καθώς και όνομα χρήστη με κωδικό πρόσβασης."
option3_header: "Επιλογή 3: Καθοδήγησε τους μαθητές σου στο codecombat.com/courses"
option3_body: "Δώσε στους μαθητές σου τον κωδικό που ακολουθεί για να το χρησιμοποιήσουν όταν θα δημιουργούν ένα λογαριασμό."
thank_you_pref: "Ευχαριστούμε για την αγορά σου! Μπορείς τώρα να εκχωρήσεις"
thank_you_suff: "και άλλους μαθητές στα μαθήματα επί πληρωμή."
return_to_class: "Επιστροφή στην Τάξη"
return_to_course_man: "Επιστροφή στη Διαχείριση Τάξης."
students_not_enrolled: "μαθητές όχι εγγεγραμμένοι"
total_all_classes: "Σύνολο σε όλες τις τάξεις"
how_many_enrollments: "Πόσες ακόμα συνδρομές θα χρειαστείς?"
each_student_access: " Κάθε μαθητής στην τάξη θα αποκτήσει πρόσβαση στα Μαθήματα 2-4 μόλις συνδεθούν με τα μαθήματα επί πληρωμή. Μπορείς να συνδέεις κάθε μάθημα με κάθε μαθητή ξεχωριστά."
purchase_now: "Αγορά Τώρα"
enrollments: "Συνδρομές"
remove_student1: "Διαγραφή Μαθητή"
are_you_sure: "Είσαι σίγουρος ότι θέλεις να διαγράψεις τον μαθητή από αυτή την τάξη?"
remove_description1: "Ο μαθητής θα χάσει την πρόσβαση σε αυτή την τάξη και στα συνδεμένα μαθήματα. Η πρόοδος και το gameplay δεν θα χαθούν, και ο μαθητής μπορεί να προστεθεί πίσω στην τάξη, ανά πάσα στιγμή."
remove_description2: "Η ενεργοποιημένη πληρωμένη συνδρομή δεν πρόκειται να επιστραφεί."
keep_student: "Keep Student"
removing_user: "Διαγραφή χρήστη"
to_join_ask: "Για να συνδεθείς με μία τάξη, ζήτα από το δάσκαλο σου τον κώδικο τάξης."
join_this_class: "Σύνδεση με Τάξη"
enter_here: "<Πληκτρόλογησε κωδικό τάξης εδώ>"
successfully_joined: "Επιτυχώς Συνδέθηκες"
click_to_start: "Κλικ εδώ για να ξεκινήσεις"
my_courses: "Μαθήματα μου"
classroom: "Τάξη"
use_school_email: "Χρησιμοποιήσε ένα email"
unique_name: "Ένα όνομα μοναδικό"
pick_something: "Διάλεξε κάτι που θα θυμάσαι"
class_code: "Κωδικός Τάξης"
optional_ask: "Προαιρετικό - ζήτα από το δάσκαλο σου να σου δώσει ένα!"
optional_school: "Προαιρετικό - σε ποιό σχολείο πηγαίνεις?"
start_playing: "Ξεκίνα να Παίζεις"
skip_this: "Πάραλειψε τοs, θα δημιουργήσω λογαριασμό αργότερα!"
welcome: "Καλώς Ήρθες"
getting_started: "Ξεκινώντας με τις Σειρές Μαθημάτων"
download_getting_started: "Κατέβασε Getting Started Guide [PDF]"
getting_started_1: "Δημιούργησε μία νέα τάξη κάνοντας κλικ στο πράσινο κουμπί <Δημιουργία Τάξης> παρακάτω."
getting_started_2: "Μόλις δημιουργήσετε την τάξη, κλικ στο μπλε κουμπί για να προσθέσετε μαθητές."
getting_started_3: "Θα βλέπεις την πρόοδο των μαθητών σου μόλις ολοκληρώσουν την εγγραφή τους και συνδεθούν με την τάξη σου."
additional_resources: "Additional Resources"
additional_resources_1_pref: "Κατέβασε/Εκτύπωσε"
additional_resources_1_mid: "Οδηγός Εκπαιδευτικού για Μάθημα 1"
additional_resources_1_suff: "Επεξηγήσεις και λύσεις για το κάθε επίπεδο."
additional_resources_2_pref: "Συμπλήρωσε την"
additional_resources_2_suff: "για να κερδίσεις 15 ακόμα ώρες υλικού Δωρεάν για 2 μήνες."
additional_resources_3_pref: "Επισκέψη"
additional_resources_3_mid: "στα Forums των εκπαιδευτικών"
additional_resources_3_suff: "για να επικοινωνήσεις με άλλους συναδέρφους που χρησιμοποιούν το CodeCombat."
your_classes: "Οι Τάξεις σου"
no_classes: "Όχι Τάξεις ακόμα!"
create_new_class1: "δημιουργία Τάξης"
available_courses: "Διαθέσιμα Μαθήματα"
unused_enrollments: "Αδιάθετες συνδρομές:"
students_access: "Όλοι οι μαθητές να έχουν πρόσβαση στην ενότητα Εισαγωγή στην Επιστήμη των Υπολογιστών δωρεάν. Μία συνδρομή ανά φοιτητή είναι υποχρεωτική για να να τους τοποθετήσετε στα μαθήματα που απαιτούν πληρωμή.Μία συνδρομή ανα μαθητή είναι αρκετή για την εγγραφή του σε όλα τα μαθήματα."
no_students: "Όχι μαθητές ακόμα!"
add_students1: "πρόσθεση μαθητών"
view_edit: "Παρακολούθηση/Επεξεργασία"
students_enrolled: "Εγγεγραμμένοι Μαθητές"
length: "Μήκος:"
classes:
archmage_title: "Αρχιμάγος"

View file

@ -438,11 +438,11 @@
parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?"
parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing."
parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?"
parents_blurb2: "For $9.99 USD/mo, your child will get new challenges every week and personal email support from professional programmers."
parents_blurb2a: "For ${{price}} USD/mo, your child will get new challenges every week and personal email support from professional programmers."
parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe."
payment_methods: "Payment Methods"
payment_methods_title: "Accepted Payment Methods"
payment_methods_blurb1: "We currently accept credit cards and Alipay. You can also PayPal 29.97 USD to nick@codecombat.com with your account email in the memo to purchase three months' subscription and gems, or $99 for a year."
payment_methods_blurb1a: "We currently accept credit cards and Alipay. You can also PayPal {{three_month_price}} USD to nick@codecombat.com with your account email in the memo to purchase three months' subscription and gems, or ${{year_price}} for a year."
payment_methods_blurb2: "If you require an alternate form of payment, please contact"
sale_already_subscribed: "You're already subscribed!"
sale_blurb1: "Save $21"
@ -461,7 +461,7 @@
sale_title: "Back to School Sale"
sale_view_button: "Buy 1 year subscription for"
stripe_description: "Monthly Subscription"
stripe_description_year_sale: "1 Year Subscription ($21 discount)"
stripe_description_year_sale1: "1 Year Subscription (${{discount}} discount)"
subscription_required_to_play: "You'll need a subscription to play this level."
unlock_help_videos: "Subscribe to unlock all video tutorials."
@ -1569,7 +1569,7 @@
email_settings_url: "your email settings"
email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time."
cost_title: "Cost"
cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee."
cost_description: "CodeCombat is free to play for all of its core levels, with a ${{price}} USD/mo subscription for access to extra level branches and {{gems}} bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee."
copyrights_title: "Copyrights and Licenses"
contributor_title: "Contributor License Agreement"
contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our"

View file

@ -14,8 +14,8 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
for_developers: "Para Desarrolladores" # Not currently shown on home page.
or_ipad: "O descargar para iPad"
hoc_class_code: "Tengo un Código de Clase"
# hoc_enter: "Enter"
# hoc_title: "Hour of Code?"
hoc_enter: "¿Entrar"
hoc_title: " a la Hora de Código?"
nav:
play: "Jugar" # The top nav bar entry where players choose which levels to play
@ -619,9 +619,9 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
cost_premium_server: "CodeCombat es gratis para lso primeros 5 niveles, después de ellos cuesta $9.99 USD mensuales para accesar a nuestros otros más de 190 niveles en nuestraos exclusivos servidores específicos para cada país."
free_1: "Hay más de 110 niveles gratis que cubren cada concepto."
free_2: "Una suscripción mensual le da acceso a tutoriales en vídeo y niveles extra para practicar."
# free_3: "The CodeCombat content is divided into"
free_3: "El contenido de CodeCombat está dividido en"
free_4: "cursos"
# free_5: ". The first course is free, and about an hour of material."
free_5: ". El primer curso es gratis, y aproximadamente una hora de material."
# free_6: "Access to the additional courses can be unlocked with a one-time purchase."
teacher_subs_title: "¡Los amestros obtienen suscripciones gratuitas!" # {change}
teacher_subs_0: "Ofrecemos suscripciones gratuitas a profesores con propósitos de evaluación." # {change}
@ -676,14 +676,14 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
approved_1: "Su aplicación a una suscripción gratuita fue" # {change}
approved_2: "aprobada." # {change}
# approved_4: "You can now enroll your students on the"
# approved_5: "courses"
approved_5: "cursos"
# approved_6: "page."
denied_1: "Su aplicación a una suscripción gratuita ha sido" # {change}
denied_2: "denegada."
contact_1: "Por favor contactarse"
contact_2: "si tiene más preguntas."
description_1: "Ofrecemos suscripciones gratuitas a maestros con propósitos de evaluación. Puede hallar más información en nuestra"
# description_1b: "You can find more information on our"
description_1b: "Puede hallar más información en nuestra"
description_2: "página"
description_3: "de maestros."
description_4: "Por favor llene esta encuesta rápida y le mandaremos por email las instrucciones de configuración."
@ -855,7 +855,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
started_2: "Iniciado"
not_started_2: "No inciiado"
view_solution: "Click para ver la solución."
# view_attempt: "Click to view attempt."
view_attempt: "Click para ver intento."
latest_achievement: "último logro"
playtime: "Tiempo de juego"
last_played: "Último jugado"

View file

@ -0,0 +1,6 @@
CocoModel = require './CocoModel'
module.exports = class MandateModel extends CocoModel
@className: 'Mandate'
@schema: require 'schemas/models/mandate.schema'
urlRoot: '/db/mandates'

View file

@ -0,0 +1,6 @@
CocoModel = require './CocoModel'
module.exports = class ProductModel extends CocoModel
@className: 'Product'
@schema: require 'schemas/models/product.schema'
urlRoot: '/db/products'

View file

@ -0,0 +1,18 @@
c = require './../schemas'
module.exports = MandateSchema = {
type: 'object'
additionalProperties: false
default:
simulationThroughputRatio: 1
properties: {
simulationThroughputRatio:
name: 'Simulation Throughput Ratio'
description: '0-1 fraction of requests for a match to simulate that should be granted.'
type: 'number'
minimum: 0
maximum: 1
}
}
c.extendBasicProperties MandateSchema, 'Mandate'

View file

@ -0,0 +1,13 @@
c = require './../schemas'
module.exports = ProductSchema = {
type: 'object'
additionalProperties: false
properties: {
name: { type: 'string' }
amount: { type: 'integer', description: 'Cost in cents' }
gems: { type: 'integer', description: 'Number of gems awarded' }
}
}
c.extendBasicProperties ProductSchema, 'Product'

View file

@ -1,30 +0,0 @@
#subscription-sale-view
.above-blurb
margin: 0 16% 0 16%
.center
text-align: center
.feature-description
font-size: 16px
.sale-blurb
font-size: 22px
.sale-ends
position: relative
left: 0px
top: 50px
font-size: 18px
line-height: 18px
background-color: red
color: white
margin: 0 7px
border-radius: 3px
padding: 6px
width: 200px
text-align: center
-ms-transform: rotate(330deg) /* IE 9 */
-moz-transform: rotate(330deg) /* Firefox */
-webkit-transform: rotate(330deg) /* Safari and Chrome */
-o-transform: rotate(330deg) /* Opera */
.sub-extended
font-size: 12px
#pay-button
font-size: 18px

186
app/styles/sales-view.sass Normal file
View file

@ -0,0 +1,186 @@
#sales-view
background-color: #F5F5F5
font-family: Helvetica, sans-serif
font-size: 15px
line-height: 20px
// TODO: better way to remove content styling?
#site-content-area
width: 100%
background-color: #F5F5F5
border: none
margin: auto
padding: 0px
min-height: inherit
.btn-contact-us
background-color: #3878DE
border: none
color: #FFFFFF
font-size: 18px
margin-top: 20px
padding: 20px
width: 330px
text-decoration: none
text-transform: uppercase
div
text-align: left
img
float: left
margin-right: 10px
.btn-create-account, .btn-enter-courses
background-color: #09AC48
color: #FFFFFF
display: inline-block
font-size: 18px
margin: 10px
padding: 24px
width: 400px
text-decoration: none
text-transform: uppercase
.btn-login-account
color: #FFFFFF
text-decoration: underline
.btn-setup-class
margin-top: 20px
text-transform: uppercase
width: 300px
.section-header
font-family: Merriweather
font-size: 23px
line-height: 29px
padding: 0px 24px 0px 24px
border-bottom: 1px solid lightgray
.section-subheader
font-family: Helvetica, sans-serif
font-size: 13px
color: #727272
line-height: 15px
.text-right
text-align: right
#top-page-content
background-image: url('/images/pages/sales/hero_background.png')
background-size: cover
color: #FFFFFF
.hero-quote-container
margin: 20px
width: 60%
.hero-quote
font-family: Merriweather
font-size: 30px
font-weight: 700
color: #FFFFFF
line-height: 37px
.hero-quote-attribution
font-family: Helvetica, sans-serif
font-style: italic
font-size: 13px
color: #FFFFFF
line-height: 16px
margin-right: 100px
text-align: right
#down-arrow
padding: 20px
#main-content
text-align: left
display: inline-block
width: 850px
#blurb1
padding: 0px 20px 0px 20px
.blurb-subtitle
font-size: 17px
font-weight: bold
#course-comparisons
font-size: 12px
margin: 20px
width: 90%
img
width: 100%
.img-caption
font-family: Helvetica, sans-serif
font-style: italic
font-size: 11px
color: #727272
line-height: 13px
.img-face
background-color: #f0e5c7
border-radius: 50%
height: 100px
width: 100px
.img-game
width: 100%
.teacher-quote
font-family: Merriweather
font-weight: 300
font-size: 18px
line-height: 20px
.teacher-name
font-family: Helvetica, sans-serif
font-weight: 700
font-size: 16px
line-height: 19px
.teacher-location
font-family: Helvetica, sans-serif
font-style: italic
font-size: 12px
line-height: 14px
#quote1-container
background-image: url('/images/pages/sales/quote1.png')
background-repeat: no-repeat
background-size: 100% auto
height: 265px
padding: 12px
margin-right: 10px
.hero-quote-attribution
margin-top: 60px
#quote2-container
background-image: url('/images/pages/sales/quote2.png')
background-repeat: no-repeat
background-size: 100% auto
height: 265px
padding: 12px
margin-left: 10px
.teacher-quote
margin-top: 60px
.hero-quote-attribution
margin-top: 20px
.twitter-attribution
font-family: Helvetica
font-weight: 700
font-size: 11px
line-height: 14px
color: black
display: inline-block
text-decoration: none

View file

@ -51,9 +51,9 @@ block content
h2(data-i18n="nav.contact")
| Contact
p
span CodeCombat, Inc.
span CodeCombat Inc.
br
span 188 King St #507
span 360 3rd St Suite 700 (Livefyre)
br
span San Francisco, CA 94107
br

View file

@ -39,7 +39,7 @@ block content
label.control-label.col-md-2(data-i18n="account_prepaid.purchase_total")
.col-md-10
p.form-control-static $
span#total #{purchase.total.toFixed(2)}
span#total #{(purchase.total/100).toFixed(2)}
button#purchase-btn.btn.btn-success.pull-right(data-i18n="account_prepaid.purchase_button")
.row
.col-md-12
@ -66,7 +66,7 @@ block content
button#redeem-code-btn.btn.btn-success(data-i18n="account_prepaid.apply_account")
.row
.col-md-12
.panel.panel-default
#codes-panel.panel.panel-default
.panel-heading
.panel-title
a(data-toggle="collapse" href="#codeslist")

View file

@ -1,76 +0,0 @@
extends /templates/base
block content
ol.breadcrumb
li
a(href="/")
span.glyphicon.glyphicon-home
li
a(href="/account", data-i18n="nav.account")
li
a(href="/account/subscription", data-i18n="account.subscription")
li.active(data-i18n="account.sale") Sale
if hasSubscription
h1(data-i18n="subscribe.sale_already_subscribed")
span.spr(data-i18n="subscribe.sale_continue")
a(href="/play", data-i18n="subscribe.sale_click_here")
else
if state === 'purchasing'
.alert.alert-info(data-i18n="account_invoices.purchasing")
else
if view.onSale
div.sale-ends
if new Date() < saleEndDate
span.spr(data-i18n="subscribe.sale_ends")
span #{moment(saleEndDate).fromNow()}
else
span(data-i18n="subscribe.sale_limited_time")
if view.onSale
h1.center(data-i18n="subscribe.sale_title")
p.center
img(src="/images/pages/account/subscription/teacher-banner.png")
br
.above-blurb
p.sale-blurb
strong.spr(data-i18n="subscribe.sale_blurb1")
span(data-i18n="subscribe.sale_blurb2")
br
div
.sale-blurb(data-i18n="subscribe.sale_feature_here")
ul
li.feature-description(data-i18n="[html]subscribe.sale_feature2")
li.feature-description(data-i18n="subscribe.feature3")
li.feature-description(data-i18n="[html]subscribe.sale_feature4")
li.feature-description(data-i18n="subscribe.feature5")
li.feature-description(data-i18n="[html]subscribe.feature7")
div.sub-extended(data-i18n="subscribe.sale_extended")
br
p.center
button.btn.btn-success#pay-button #{payButtonText}
table.above-blurb(cellpadding="8")
tr
td(colspan=2)
h2.center(data-i18n="subscribe.sale_new_heroes")
each hero in heroes
if hero.get('extendedName') !== 'Captain Anya Weston' && hero.get('extendedName') !== 'Sir Tharin Thunderfist'
tr
td
img(src="http://codecombat.com#{hero.getPortraitURL()}")
td
p.sale-blurb #{hero.name}
p #{hero.class}
p #{hero.description}
br
if state === 'declined'
#declined-alert.alert.alert-danger.alert-dismissible
span(data-i18n="account_invoices.declined")
button.close(type="button" data-dismiss="alert")
span(aria-hidden="true") &times;
if state === 'unknown_error'
#error-alert.alert.alert-danger.alert-dismissible
button.close(type="button" data-dismiss="alert")
span(aria-hidden="true") &times;
p(data-i18n="loading_error.unknown")
p= stateMessage

View file

@ -133,54 +133,12 @@ block content
.panel.panel-default
.panel-heading
h3(data-i18n="subscribe.managed_subs")
p(data-i18n="subscribe.managed_subs_desc")
p(data-i18n="subscribe.managed_subs_desc_2")
h4(data-i18n="subscribe.group_discounts")
table.table.table-striped.table-condensed.discount-table
tr
td(data-i18n="subscribe.group_discounts_1st")
td(data-i18n="subscribe.group_discounts_full")
tr
td(data-i18n="subscribe.group_discounts_2nd")
td(data-i18n="subscribe.group_discounts_20")
tr
td(data-i18n="subscribe.group_discounts_12th")
td(data-i18n="subscribe.group_discounts_40")
p
span.spr Managed subscriptions are discontinued. Please contact
a(href='mailto:team@codecombat.com') team@codecombat.com
span.spl for more information.
.panel-body
if view.recipientSubs.state === 'subscribing'
.alert.alert-info(data-i18n="subscribe.subscribing")
else
if emailValidator.state === 'invalid'
div.invalid-email-message(aria-hidden="true") please make sure all entries are valid emails
textarea.recipient-emails(rows=3, data-i18n="[placeholder]subscribe.recipient_emails_placeholder")=emailValidator.lastEmails
div
button.recipients-subscribe-button.btn.btn-lg.btn-success(data-i18n="subscribe.subscribe_users")
if view.recipientSubs.state === 'declined'
br
.alert.alert-danger.alert-dismissible
span(data-i18n="buy_gems.declined")
button.close(type="button" data-dismiss="alert")
span(aria-hidden="true") &times;
else if view.recipientSubs.state === 'unknown_error'
br
.alert.alert-danger.alert-dismissible
button.close(type="button" data-dismiss="alert")
span(aria-hidden="true") &times;
p(data-i18n="loading_error.unknown")
p= view.recipientSubs.stateMessage
else if view.recipientSubs.justSubscribed && view.recipientSubs.justSubscribed.length > 0
br
.alert.alert-success.alert-dismissible
if view.recipientSubs.justSubscribed.length > 0
div(data-i18n="subscribe.users_subscribed")
ul
each email in view.recipientSubs.justSubscribed
li= email
else if view.recipientSubs.justSubscribed && view.recipientSubs.justSubscribed.length === 0
br
.alert.alert-success.alert-dismissible
div(data-i18n="subscribe.no_users_subscribed")
if view.recipientSubs.nextPaymentAmount > 0 && view.recipientSubs.sponsorSub
h4(data-i18n="account.next_payment")
p= moment(new Date(view.recipientSubs.sponsorSub.current_period_end * 1000)).format('l')

View file

@ -23,9 +23,12 @@
if !me.isOnPremiumServer()
th.free-cell(data-i18n="subscribe.free")
th
//- TODO: find a better way to localize '$9.99/month'
span $#{(view.product.amount / 100)}/
span(data-i18n="subscribe.month")
//- TODO: find a better way to localize price
if view.basicProduct
span $#{(view.basicProduct.get('amount') / 100)}/
span(data-i18n="subscribe.month")
else
span '...'
tbody
tr
td.feature-description

View file

@ -82,7 +82,7 @@ block content
span(data-i18n="account_prepaid.purchase_total")
span.spr : #{view.numberOfStudents}
span(data-i18n="courses.enrollments")
span.spl x $#{view.pricePerStudent.toFixed(2)} = #{view.getPriceString()}
span.spl x $#{(view.pricePerStudent/100).toFixed(2)} = #{view.getPriceString()}
p.text-center
button#purchase-btn.btn.btn-lg.btn-success.uppercase(data-i18n="courses.purchase_now")

View file

@ -52,7 +52,7 @@ block content
if !me.isOnPremiumServer()
h4(data-i18n="legal.cost_title")
| Cost
p(data-i18n="legal.cost_description")
p#cost-description(data-i18n="legal.cost_description")
| CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee.
h2(data-i18n="legal.copyrights_title")

View file

@ -1,10 +1,10 @@
.modal-dialog
.modal-content
if state === 'purchasing'
if view.state === 'purchasing'
.alert.alert-info(data-i18n="buy_gems.purchasing")
else if state === 'retrying'
else if view.state === 'retrying'
#retrying-alert.alert.alert-danger(data-i18n="buy_gems.retrying")
else
@ -12,12 +12,12 @@
h1(data-i18n="play.buy_gems")
#products
for product in products
for product in view.products.models
.product
h4 x#{product.gems}
h3(data-i18n=product.i18n)
button.btn.btn-illustrated.btn-lg(value=product.id)
span= product.price
h4 x#{product.get('gems')}
h3(data-i18n=product.get('i18n'))
button.btn.btn-illustrated.btn-lg(value=product.get('name'))
span= product.get('priceString')
.product
h4(data-i18n="buy_gems.price") x3500 / mo
@ -29,20 +29,20 @@
else
button.start-subscription-button.btn.btn-lg.btn-illustrated.btn-success(data-i18n="subscribe.subscribe_title") Subscribe
if state === 'declined'
if view.state === 'declined'
#declined-alert.alert.alert-danger.alert-dismissible
span(data-i18n="buy_gems.declined")
button.close(type="button" data-dismiss="alert")
span(aria-hidden="true") &times;
if state === 'unknown_error'
if view.state === 'unknown_error'
#error-alert.alert.alert-danger.alert-dismissible
button.close(type="button" data-dismiss="alert")
span(aria-hidden="true") &times;
p(data-i18n="loading_error.unknown")
p= stateMessage
p= view.stateMessage
if state === 'recovered_charge'
if view.state === 'recovered_charge'
#recovered-alert.alert.alert-danger.alert-dismissible
span(data-i18n="buy_gems.recovered")
button.close(type="button" data-dismiss="alert")

View file

@ -0,0 +1,212 @@
extends /templates/base
//- Do NOT localize / i18n
block content
#top-page-content
br
br
.text-right
button.btn-contact-us(href='/teachers/freetrial')
img(src='/images/pages/sales/chat_icon.png')
div contact us for pricing and a free trial
.hero-quote-container
.hero-quote "CodeCombat is without question the most engaging platform for learning programming languages."
.hero-quote-attribution Jonathan P., Elementary Computers Teacher
if me.isAnonymous()
.text-center
a.btn-create-account set up a free class
.text-center
span.spr Already have an account?
a.btn-login-account Log in here.
else
.text-center
a.btn-enter-courses(href="/courses/teachers") set up a free class
p.text-center
a(href='#getting-started')
img#down-arrow(src='/images/pages/sales/down_arrow.png')
a(name="getting-started")
.text-center
#main-content
br
p.text-center
span.section-header What are CodeCombat Courses?
p.text-center.section-subheader An entire semesters' worth of computer science curriculum for teachers who want to bring programming into their 4th-12th grade classes.
br
.row
.col-md-5
img#img-game(src='/images/pages/sales/screen1.png')
.text-center
i
small Students learn by writing code to complete levels in the game.
.col-md-7
#blurb1
//- TODO: why don't jade inline tags work here?
//- http://stackoverflow.com/questions/10953326/what-is-a-concise-way-to-create-inline-elements-in-jade
p
strong.spr CodeCombat
span.spr is a platform for students to learn programming all while playing a game with their classmates. We believe in encouraging
strong.spr real, typed code
span to support healthy learning curve with programming.
p
span.spr CodeCombat's Courses consist of levels that have been specifically playtested to work best in a classroom setting, designed to be used by teachers with
strong.spr no prior coding experience necessary.
span Current Courses are available in JavaScript and Python, with solution guides provided for both.
br
br
.row
.col-md-7
#blurb2
p.text-center.blurb-subtitle Designed with Teachers in Mind
ul
li Intuitive dashboard to track student progress
li Course-specific teacher guides with full solutions to each levels
li Teachers' forum and community-generated lesson plans.
li Optional leaderboards to encourage friendly competitions
li
span.spr Auto-generate student progress reports
i (coming soon!)
li
span.spr Multiple administrative accounts
i (coming soon!)
.col-md-5
img.img-game(src='/images/pages/sales/screen2.png')
.text-center
i
small Teachers can monitor progress, manage classrooms and more.
if me.isAnonymous()
br
.text-center
a.btn-create-account set up a free class
br
br
p.text-center
span.section-header Students (and parents) love us!
br
table
tr
td
#quote1-container
.teacher-quote
span.spr "My class had been struggling with the basics of Python all year. Today they were having fun and getting into it
strong - I think they actually forgot that they were actually learning something."
.row
.col-md-4
.col-md-8
.hero-quote-attribution
.teacher-name Tim M.
.teacher-location Director of Technology, Tilton School
td
#quote2-container
.row
.col-md-8
.hero-quote-attribution
.teacher-name.text-right Darlease M.
.teacher-location.text-right Technology Coordinator,
.teacher-location.text-right Global Learning Charter Public School
.col-md-4
.teacher-quote
span.spr "My girls, who were apprehensive about taking a coding class, ar some of my top students.
strong They work together and explain the code to each other to make sure each understands."
br
.text-center.section-subheader Students play CodeCombat during #HourOfCode 2015
table(cellpadding=4)
tr
td
img(src='/images/pages/sales/classroom3.png')
.text-right
a(href='https://twitter.com/flinng/status/674238468747354112')
img(src='/images/twitter_icon.png')
span.twitter-attribution @flinng
td
img(src='/images/pages/sales/classroom4.png')
.text-right
a(href='https://twitter.com/HikariKishi/status/674359511566577664')
img(src='/images/twitter_icon.png')
span.twitter-attribution @HikariKishi
td
img(src='/images/pages/sales/classroom2.png')
.text-right
a(href='https://twitter.com/Coderdojovno/status/675743290461941760')
img(src='/images/twitter_icon.png')
span.twitter-attribution @Coderdojovno
table(cellpadding=4)
tr
td
img(src='/images/pages/sales/classroom6.png')
.text-right
a(href='https://twitter.com/teachercoulter/status/674734565487828992')
img(src='/images/twitter_icon.png')
span.twitter-attribution @teachercoulter
td
img(src='/images/pages/sales/classroom1.png')
.text-right
a(href='https://twitter.com/CentreHigh/status/674643613360324608')
img(src='/images/twitter_icon.png')
span.twitter-attribution @CentreHigh
td
img(src='/images/pages/sales/classroom5.png')
.text-right
a(href='https://twitter.com/MrsYassen_GRE/status/674747949902090244')
img(src='/images/twitter_icon.png')
span.twitter-attribution @MrsYassen_GRE
br
p.blurb-subtitle Facts about CodeCombat:
.row
.col-md-6
ul
li Students collaborate and help each other solve levels.
li Appeals to all genders and a wide range of age groups.
.cold-md-6
ul
li Typed code gives students an advantage over block-based programs.
li Success with both high-performing and low-performing students, as well as ESL.
br
.row
.col-md-8
p.text-center
span.section-header What are paid courses?
p The one-hour long "Introduction to Computer Science" course will always be free for an unlimited number of students. Paid enrollments allow each student access to Computer Science 2, 3, and 4, which is an additional 15 hours of content on top of the free contnet. Paid enrollments never expire.
#course-comparisons
img(src='/images/pages/sales/content_table.png')
br
p Per-student pricing allows us to better support flexibility for both large and small schools. Contact us if you are interested in purchasing paid course enrollments for your classroom or school. Free trials are also available upon request.
.col-md-4
.well
p.text-center.blurb-subtitle Resources for Teachers
p
a(href='http://codecombat.com/docs/CodeCombatHourofCodeGettingStartedGuide.pdf')
img(src='/images/Adobe_PDF_file_icon_32x32.png')
span Getting Started Guide
p
a(href='http://codecombat.com/docs/CodeCombatTeacherGuideCourse1.pdf')
img(style='float:left;', src='/images/Adobe_PDF_file_icon_32x32.png')
span Introduction to Computer Science Teacher's Guide
br
p
i
small Solution guides and additional lesson plans available for paid courses.
p
i
small Contact team@codecombat.com with additional requests.
br
br
p.text-center
button.btn-contact-us contact us for a free trial
br

View file

@ -14,7 +14,7 @@ module.exports = class HomeView extends RootView
if @getQueryVariable 'hour_of_code'
application.router.navigate "/hoc", trigger: true
isHourOfCodeWeek = true # Temporary: default to /hoc flow during the main event week
isHourOfCodeWeek = false # Temporary: default to /hoc flow during the main event week
if isHourOfCodeWeek and (@isNewPlayer() or (@justPlaysCourses() and me.isAnonymous()))
# Go/return straight to playing single-player HoC course on Play click
@playURL = '/hoc?go=true'

View file

@ -1,6 +1,26 @@
RootView = require 'views/core/RootView'
template = require 'templates/legal'
Products = require 'collections/Products'
module.exports = class LegalView extends RootView
id: 'legal-view'
template: template
initialize: ->
@products = new Products()
@supermodel.loadCollection(@products, 'products')
onLoaded: ->
basicSub = @products.findWhere({name: 'basic_subscription'})
@price = (basicSub.get('amount') / 100).toFixed(2)
@gems = basicSub.get('gems')
super()
afterRender: ->
super()
# TODO: Figure out how to use i18n interpolation in this case
$el = @$('#cost-description')
f = =>
$el.text($el.text().replace('{{price}}', @price))
$el.text($el.text().replace('{{gems}}', @gems))
_.defer(f) # i18n call gets made immediately after render

View file

@ -0,0 +1,27 @@
app = require 'core/application'
AuthModal = require 'views/core/AuthModal'
RootView = require 'views/core/RootView'
template = require 'templates/sales-view'
module.exports = class SalesView extends RootView
id: 'sales-view'
template: template
events:
'click .btn-contact-us': 'onClickContactUs'
'click .btn-create-account': 'onClickSignup'
'click .btn-login-account': 'onClickLogin'
getTitle: ->
'CodeCombat'
onClickContactUs: (e) ->
app.router.navigate '/teachers/freetrial', trigger: true
onClickLogin: (e) ->
@openModalView new AuthModal(mode: 'login') if me.get('anonymous')
window.tracker?.trackEvent 'Started Login', category: 'Sales', label: 'Sales Login'
onClickSignup: (e) ->
@openModalView new AuthModal() if me.get('anonymous')
window.tracker?.trackEvent 'Started Signup', category: 'Sales', label: 'Sales Create'

View file

@ -7,6 +7,7 @@ Prepaid = require '../../models/Prepaid'
utils = require 'core/utils'
RedeemModal = require 'views/account/PrepaidRedeemModal'
forms = require 'core/forms'
Products = require 'collections/Products'
# TODO: remove redeem code modal
@ -26,12 +27,10 @@ module.exports = class PrepaidView extends RootView
subscriptions:
'stripe:received-token': 'onStripeReceivedToken'
baseAmount: 9.99
constructor: (options) ->
super(options)
@purchase =
total: @baseAmount
total: 0
users: 3
months: 3
@updateTotal()
@ -45,6 +44,14 @@ module.exports = class PrepaidView extends RootView
@ppcQuery = true
@loadPrepaid(@ppc)
@products = new Products()
@supermodel.loadCollection(@products, 'products')
onLoaded: ->
@prepaidProduct = @products.findWhere { name: 'prepaid_subscription' }
@updateTotal()
super()
getRenderData: ->
c = super()
c.purchase = @purchase
@ -62,7 +69,8 @@ module.exports = class PrepaidView extends RootView
noty text: message, layout: 'topCenter', type: type, killer: false, timeout: 5000, dismissQueue: true, maxVisible: 3
updateTotal: ->
@purchase.total = getPrepaidCodeAmount(@baseAmount, @purchase.users, @purchase.months)
return unless @prepaidProduct
@purchase.total = getPrepaidCodeAmount(@prepaidProduct.get('amount'), @purchase.users, @purchase.months)
@renderSelectors("#total", "#users-input", "#months-input")
# Form Input Callbacks
@ -99,7 +107,7 @@ module.exports = class PrepaidView extends RootView
onClickPurchaseButton: (e) ->
return unless $("#users-input").val() >= 3 or $("#months-input").val() >= 3
@purchaseTimestamp = new Date().getTime()
@stripeAmount = @purchase.total * 100
@stripeAmount = @purchase.total
@description = "Prepaid Code for " + @purchase.users + " users / " + @purchase.months + " months"
stripeHandler.open
@ -179,6 +187,7 @@ module.exports = class PrepaidView extends RootView
# console.log 'SUCCESS: Prepaid purchase', model.code
@statusMessage "Successfully purchased Prepaid Code!", "success"
@codes.add(model)
@renderSelectors('#codes-panel')
@statusMessage "Finalizing purchase...", "information"
@supermodel.addRequestResource('purchase_prepaid', options, 0).load()

View file

@ -1,92 +0,0 @@
RootView = require 'views/core/RootView'
template = require 'templates/account/subscription-sale-view'
app = require 'core/application'
AuthModal = require 'views/core/AuthModal'
CocoCollection = require 'collections/CocoCollection'
stripeHandler = require 'core/services/stripe'
ThangType = require 'models/ThangType'
utils = require 'core/utils'
module.exports = class SubscriptionSaleView extends RootView
id: "subscription-sale-view"
template: template
yearSaleAmount: 9900
saleEndDate: new Date('2015-09-05')
onSale: false
events:
'click #pay-button': 'onPayButton'
subscriptions:
'stripe:received-token': 'onStripeReceivedToken'
constructor: (options) ->
super(options)
@description = $.i18n.t('subscribe.stripe_description_year_sale')
displayAmount = (@yearSaleAmount / 100).toFixed(2)
@payButtonText = "#{$.i18n.t('subscribe.sale_view_button')} $#{displayAmount}"
@heroes = new CocoCollection([], {model: ThangType})
@heroes.url = '/db/thang.type?view=heroes'
@heroes.setProjection ['original','name','heroClass','description', 'gems','extendedName','i18n']
@heroes.comparator = 'gems'
@listenToOnce @heroes, 'sync', @onHeroesLoaded
@supermodel.loadCollection(@heroes, 'heroes')
getRenderData: ->
c = super()
c.hasSubscription = me.get('stripe')?.sponsorID
c.heroes = @heroes.models
c.payButtonText = @payButtonText
c.saleEndDate = @saleEndDate
c.state = @state
c.stateMessage = @stateMessage
c
onHeroesLoaded: ->
@formatHero hero for hero in @heroes.models
formatHero: (hero) ->
hero.name = utils.i18n hero.attributes, 'extendedName'
hero.name ?= utils.i18n hero.attributes, 'name'
hero.description = utils.i18n hero.attributes, 'description'
hero.class = hero.get('heroClass') or 'Warrior'
onPayButton: ->
return @openModalView new AuthModal() if me.isAnonymous()
@state = undefined
@stateMessage = undefined
@render()
# Show Stripe handler
application.tracker?.trackEvent 'Started sale landing page subscription purchase'
@timestampForPurchase = new Date().getTime()
stripeHandler.open
amount: @yearSaleAmount
description: @description
bitcoin: true
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
onStripeReceivedToken: (e) ->
@state = 'purchasing'
@render?()
# Call year sale API
data =
stripe:
token: e.token.id
timestamp: @timestampForPurchase
jqxhr = $.post('/db/subscription/-/year_sale', data)
jqxhr.done (data, textStatus, jqXHR) =>
application.tracker?.trackEvent 'Finished sale landing page subscription purchase', value: @yearSaleAmount
me.fetch(cache: false).always =>
app.router.navigate '/play', trigger: true
jqxhr.fail (xhr, textStatus, errorThrown) =>
console.error 'We got an error subscribing with Stripe from our server:', textStatus, errorThrown
application.tracker?.trackEvent 'Failed to finish 1 year subscription purchase', status: textStatus
if xhr.status is 402
@state = 'declined'
@stateMessage = arguments[2]
else
@state = 'unknown_error'
@stateMessage = "#{xhr.status}: #{xhr.responseText}"
@render?()

View file

@ -131,7 +131,7 @@ module.exports = class DiplomatView extends ContributeClassView
hi: [] # ि, Hindi
ur: [] # اُردُو, Urdu
ms: [] # Bahasa Melayu, Bahasa Malaysia
ca: ['ArniMcFrag'] # Català, Catalan
ca: ['ArniMcFrag', 'Nainufar'] # Català, Catalan
gl: ['mcaeiror'] # Galego, Galician
'mk-MK': ['SuperPranx'] # Македонски, Macedonian
eo: [] # Esperanto, Esperanto

View file

@ -3,16 +3,14 @@ template = require 'templates/core/subscribe-modal'
stripeHandler = require 'core/services/stripe'
utils = require 'core/utils'
AuthModal = require 'views/core/AuthModal'
Products = require 'collections/Products'
module.exports = class SubscribeModal extends ModalView
id: 'subscribe-modal'
template: template
plain: true
closesOnClickOutside: false
product:
amount: 999
planID: 'basic'
yearAmount: 9900
planID: 'basic'
subscriptions:
'stripe:received-token': 'onStripeReceivedToken'
@ -27,6 +25,13 @@ module.exports = class SubscribeModal extends ModalView
constructor: (options) ->
super(options)
@state = 'standby'
@products = new Products()
@supermodel.loadCollection(@products, 'products')
onLoaded: ->
@basicProduct = @products.findWhere { name: 'basic_subscription' }
@yearProduct = @products.findWhere { name: 'year_subscription' }
super()
afterRender: ->
super()
@ -51,11 +56,15 @@ module.exports = class SubscribeModal extends ModalView
application.tracker?.trackEvent 'Subscription ask parent button click'
setupParentInfoPopover: ->
return unless @products.size()
popoverTitle = $.i18n.t 'subscribe.parents_title'
levelsCompleted = me.get('stats')?.gamesCompleted or 'several'
popoverContent = "<p>" + $.i18n.t('subscribe.parents_blurb1', nLevels: levelsCompleted) + "</p>"
popoverContent += "<p>" + $.i18n.t('subscribe.parents_blurb1a') + "</p>"
popoverContent += "<p>" + $.i18n.t('subscribe.parents_blurb2') + "</p>"
popoverContent += "<p>" + $.i18n.t('subscribe.parents_blurb2a') + "</p>"
price = (@products.findWhere({'name': 'basic_subscription'}).get('amount') / 100).toFixed(2)
# TODO: Update i18next and use its own interpolation system instead
popoverContent = popoverContent.replace('{{price}}', price)
popoverContent += "<p>" + $.i18n.t('subscribe.parents_blurb3') + "</p>"
#popoverContent = popoverContent.replace /9[.,]99/g, '3.99' # Sale
@$el.find('#parents-info').popover(
@ -70,9 +79,15 @@ module.exports = class SubscribeModal extends ModalView
application.tracker?.trackEvent 'Subscription parent hover'
setupPaymentMethodsInfoPopover: ->
return unless @products.size()
popoverTitle = $.i18n.t('subscribe.payment_methods_title')
three_month_price = (@products.findWhere({'name': 'basic_subscription'}).get('amount') * 3 / 100).toFixed(2)
year_price = (@products.findWhere({name: 'year_subscription'}).get('amount') / 100).toFixed(2)
popoverTitle += '<button type="button" class="close" onclick="$(&#39;#payment-methods-info&#39;).popover(&#39;hide&#39;);">&times;</button>'
popoverContent = "<p>" + $.i18n.t('subscribe.payment_methods_blurb1') + "</p>"
popoverContent = "<p>" + $.i18n.t('subscribe.payment_methods_blurb1a') + "</p>"
# TODO: Update i18next and use its own interpolation system instead
popoverContent = popoverContent.replace('{{three_month_price}}', three_month_price)
popoverContent = popoverContent.replace('{{year_price}}', year_price)
popoverContent += "<p>" + $.i18n.t('subscribe.payment_methods_blurb2') + " <a href='mailto:support@codecombat.com'>support@codecombat.com</a>."
@$el.find('#payment-methods-info').popover(
animation: true
@ -109,12 +124,13 @@ module.exports = class SubscribeModal extends ModalView
@$el.find('.parent-button').popover('hide')
onClickPurchaseButton: (e) ->
return unless @basicProduct and @yearProduct
@playSound 'menu-button-click'
return @openModalView new AuthModal() if me.get('anonymous')
application.tracker?.trackEvent 'Started subscription purchase'
options = {
description: $.i18n.t('subscribe.stripe_description')
amount: @product.amount
amount: @basicProduct.get('amount')
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
alipayReusable: true
}
@ -136,9 +152,11 @@ module.exports = class SubscribeModal extends ModalView
@playSound 'menu-button-click'
return @openModalView new AuthModal() if me.get('anonymous')
application.tracker?.trackEvent 'Started 1 year subscription purchase'
discount = @basicProduct.get('amount') * 12 - @yearProduct.get('amount')
discountString = (discount/100).toFixed(2)
options =
description: $.i18n.t('subscribe.stripe_description_year_sale')
amount: @product.yearAmount
description: $.i18n.t('subscribe.stripe_description_year_sale1').replace('{{discount}}', discountString)
amount: @yearProduct.get('amount')
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
alipayReusable: true
@purchasedAmount = options.amount
@ -148,15 +166,15 @@ module.exports = class SubscribeModal extends ModalView
@state = 'purchasing'
@render()
if @purchasedAmount is @product.amount
if @purchasedAmount is @basicProduct.get('amount')
stripe = _.clone(me.get('stripe') ? {})
stripe.planID = @product.planID
stripe.planID = @basicProduct.get('planID')
stripe.token = e.token.id
me.set 'stripe', stripe
@listenToOnce me, 'sync', @onSubscriptionSuccess
@listenToOnce me, 'error', @onSubscriptionError
me.patch({headers: {'X-Change-Plan': 'true'}})
else if @purchasedAmount is @product.yearAmount
else if @purchasedAmount is @yearProduct.get('amount')
# Purchasing a year
data =
stripe:

View file

@ -9,12 +9,13 @@ stripeHandler = require 'core/services/stripe'
template = require 'templates/courses/purchase-courses-view'
User = require 'models/User'
utils = require 'core/utils'
Products = require 'collections/Products'
module.exports = class PurchaseCoursesView extends RootView
id: 'purchase-courses-view'
template: template
numberOfStudents: 30
pricePerStudent: 4
pricePerStudent: 0
initialize: (options) ->
@listenTo stripeHandler, 'received-token', @onStripeReceivedToken
@ -29,13 +30,19 @@ module.exports = class PurchaseCoursesView extends RootView
@prepaids.comparator = '_id'
@prepaids.fetchByCreator(me.id)
@supermodel.loadCollection(@prepaids, 'prepaids')
@products = new Products()
@supermodel.loadCollection(@products, 'products')
super(options)
events:
'input #students-input': 'onInputStudentsInput'
'click #purchase-btn': 'onClickPurchaseButton'
onLoaded: ->
@pricePerStudent = @products.findWhere({name: 'course'}).get('amount')
super()
getPriceString: -> '$' + (@getPrice()).toFixed(2)
getPriceString: -> '$' + (@getPrice()/100).toFixed(2)
getPrice: -> @pricePerStudent * @numberOfStudents
onceClassroomsSync: ->
@ -80,7 +87,7 @@ module.exports = class PurchaseCoursesView extends RootView
application.tracker?.trackEvent 'Started course prepaid purchase', {
price: @pricePerStudent, students: @numberOfStudents}
stripeHandler.open
amount: @numberOfStudents * @pricePerStudent * 100
amount: @numberOfStudents * @pricePerStudent
description: "Full course access for #{@numberOfStudents} students"
bitcoin: true
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'

View file

@ -26,9 +26,8 @@ module.exports = class SimulateTabView extends CocoView
onLoaded: ->
super()
@render()
# Save our MongoDB oplog!
#if (document.location.hash is '#simulate' or @options.level.get('type') is 'course-ladder') and not @simulator
# @startSimulating()
if (document.location.hash is '#simulate' or @options.level.get('type') is 'course-ladder') and not @simulator
@startSimulating()
getRenderData: ->
ctx = super()

View file

@ -427,7 +427,7 @@ module.exports = class PlayLevelView extends RootView
shouldSimulate: ->
return true if @getQueryVariable('simulate') is true
return false # Save our MongoDB oplog!
return false if @getQueryVariable('simulate') is false
stillBuggy = true # Keep this true while we still haven't fixed the zombie worker problem when simulating the more difficult levels on Chrome
defaultCores = 2
cores = window.navigator.hardwareConcurrency or defaultCores # Available on Chrome/Opera, soon Safari

View file

@ -497,7 +497,9 @@ module.exports = class InventoryModal extends ModalView
@playSound 'menu-button-click'
@showLoading()
ua = navigator.userAgent.toLowerCase()
unless me.isAdmin() or hasGoneFullScreenOnce and (/safari/.test(ua) and not /chrome/.test(ua)) or $(window).height() >= 658 # Min vertical resolution needed at 1366px wide
isSafari = /safari/.test(ua) and not /chrome/.test(ua)
isTooShort = $(window).height() < 658 # Min vertical resolution needed at 1366px wide
if isTooShort and not me.isAdmin() and not hasGoneFullScreenOnce and not isSafari
@toggleFullscreen()
hasGoneFullScreenOnce = true
@updateConfig =>

View file

@ -3,18 +3,13 @@ template = require 'templates/play/modal/buy-gems-modal'
stripeHandler = require 'core/services/stripe'
utils = require 'core/utils'
SubscribeModal = require 'views/core/SubscribeModal'
Products = require 'collections/Products'
module.exports = class BuyGemsModal extends ModalView
id: 'buy-gems-modal'
template: template
plain: true
originalProducts: [
{ price: '$4.99', gems: 5000, amount: 499, id: 'gems_5', i18n: 'buy_gems.few_gems' }
{ price: '$9.99', gems: 11000, amount: 999, id: 'gems_10', i18n: 'buy_gems.pile_gems' }
{ price: '$19.99', gems: 25000, amount: 1999, id: 'gems_20', i18n: 'buy_gems.chest_gems' }
]
subscriptions:
'ipad:products': 'onIPadProducts'
'ipad:iap-complete': 'onIAPComplete'
@ -29,22 +24,21 @@ module.exports = class BuyGemsModal extends ModalView
super(options)
@timestampForPurchase = new Date().getTime()
@state = 'standby'
@products = new Products()
@products.comparator = 'amount'
if application.isIPadApp
@products = []
Backbone.Mediator.publish 'buy-gems-modal:update-products'
else
@products = @originalProducts
@supermodel.loadCollection(@products, 'products')
$.post '/db/payment/check-stripe-charges', (something, somethingElse, jqxhr) =>
if jqxhr.status is 201
@state = 'recovered_charge'
@render()
getRenderData: ->
c = super()
c.products = @products
c.state = @state
c.stateMessage = @stateMessage
return c
onLoaded: ->
@products.reset @products.filter (product) -> _.string.startsWith(product.get('name'), 'gems_')
super()
afterRender: ->
super()
@ -56,19 +50,20 @@ module.exports = class BuyGemsModal extends ModalView
@playSound 'game-menu-close'
onIPadProducts: (e) ->
newProducts = []
for iapProduct in e.products
localProduct = _.find @originalProducts, { id: iapProduct.id }
continue unless localProduct
localProduct.price = iapProduct.price
newProducts.push localProduct
@products = _.sortBy newProducts, 'gems'
@render()
# TODO: Update to handle new products collection
# newProducts = []
# for iapProduct in e.products
# localProduct = _.find @originalProducts, { id: iapProduct.id }
# continue unless localProduct
# localProduct.price = iapProduct.price
# newProducts.push localProduct
# @products = _.sortBy newProducts, 'gems'
# @render()
onClickProductButton: (e) ->
@playSound 'menu-button-click'
productID = $(e.target).closest('button').val()
product = _.find @products, { id: productID }
product = @products.findWhere { name: productID }
if application.isIPadApp
Backbone.Mediator.publish 'buy-gems-modal:purchase-initiated', { productID: productID }
@ -76,8 +71,8 @@ module.exports = class BuyGemsModal extends ModalView
else
application.tracker?.trackEvent 'Started gem purchase', { productID: productID }
stripeHandler.open({
description: $.t(product.i18n)
amount: product.amount
description: $.t(product.get('i18n'))
amount: product.get('amount')
bitcoin: true
alipay: if me.get('country') is 'china' or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
})
@ -86,7 +81,7 @@ module.exports = class BuyGemsModal extends ModalView
onStripeReceivedToken: (e) ->
data = {
productID: @productBeingPurchased.id
productID: @productBeingPurchased.get('name')
stripe: {
token: e.token.id
timestamp: @timestampForPurchase
@ -97,8 +92,8 @@ module.exports = class BuyGemsModal extends ModalView
jqxhr = $.post('/db/payment', data)
jqxhr.done(=>
application.tracker?.trackEvent 'Finished gem purchase',
productID: @productBeingPurchased.id
value: @productBeingPurchased.amount
productID: @productBeingPurchased.get('name')
value: @productBeingPurchased.get('amount')
document.location.reload()
)
jqxhr.fail(=>
@ -116,7 +111,7 @@ module.exports = class BuyGemsModal extends ModalView
)
onIAPComplete: (e) ->
product = _.find @products, { id: e.productID }
product = @products.findWhere { name: e.productID }
purchased = me.get('purchased') ? {}
purchased = _.clone purchased
purchased.gems ?= 0

View file

@ -1,65 +1,41 @@
// Average level playtimes by campaign
// Average level playtimes in seconds by campaign, broken up by course and campaign levels
// Usage:
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
// NOTE: faster to use find() instead of aggregate()
// NOTE: faster to ask for one level at a time. also keeps levels in campaign order
// NOTE: faster to ask for one level at a time.
// Excluded for one reason or another
// Some relevant code: https://github.com/codecombat/codecombat/blob/master/app/views/play/CampaignView.coffee#L281-L292
var excludedLevels = ['deadly-dungeon-rescue', 'kithgard-brawl', 'cavern-survival', 'kithgard-mastery', 'destroying-angel', 'kithgard-apprentice', 'wild-horses', 'lost-viking', 'forest-flower-grove', 'boulder-woods', 'the-trials'];
var courseCampaigns = ['intro', 'course-2', 'course-3', 'course-4'];
var individualCampaigns = ['dungeon', 'forest', 'desert', 'mountain'];
var scriptStartTime = new Date();
var startDay = '2015-07-01';
var endDay = '2015-08-06';
var startDay = '2015-12-06';
var endDay = '2015-12-13';
log("Dates: " + startDay + " to " + endDay);
print("Dates: " + startDay + " to " + endDay);
// Print out playtimes for each campaign
var campaigns = getCampaigns();
var campaigns = getCampaigns(courseCampaigns);
for (var i = 0; i < campaigns.length; i++) {
var campaign = campaigns[i];
// if (campaign.slug !== 'dungeon') continue;
print(campaign.slug + " (free)");
var total = 0;
for (var j = 0; j < campaign.free.length; j++) {
var levelSlug = campaign.free[j];
if (excludedLevels.indexOf(levelSlug) >= 0) continue;
var data = getPlaytimes([levelSlug]);
print(data[levelSlug].average + "\t" + data[levelSlug].count + "\t" + levelSlug);
total += data[levelSlug];
}
// print(parseInt(total/60/60) + "\t\t total hours");
total = 0;
print(campaign.slug + " (paid)");
for (var j = 0; j < campaign.paid.length; j++) {
var levelSlug = campaign.paid[j];
if (excludedLevels.indexOf(levelSlug) >= 0) continue;
var data = getPlaytimes([levelSlug]);
if (data[levelSlug]) {
print(data[levelSlug].average + "\t" + data[levelSlug].count + "\t" + levelSlug);
total += data[levelSlug];
print(campaign.slug);
print("Sessions\tAverage\tSessions\tAverage\tLevel");
for (var j = 0; j < campaign.levelSlugs.length; j++) {
var levelSlug = campaign.levelSlugs[j];
var levelPlaytimes = getPlaytimes([levelSlug]);
if (levelPlaytimes[levelSlug]) {
print(levelPlaytimes[levelSlug].campaign.count,
'\t', levelPlaytimes[levelSlug].campaign.average,
'\t', levelPlaytimes[levelSlug].course.count,
'\t', levelPlaytimes[levelSlug].course.average,
'\t', levelSlug);
}
else {
print("0\t0\t" + levelSlug);
print(0, '\t', 0, '\t', 0, '\t', 0, '\t', levelSlug);
}
}
// print(parseInt(total/60/60) + "\t\t total hours");
total = 0;
print(campaign.slug + " (replayable)");
for (var j = 0; j < campaign.replayable.length; j++) {
var levelSlug = campaign.replayable[j];
if (excludedLevels.indexOf(levelSlug) >= 0) continue;
var data = getPlaytimes([levelSlug]);
print(data[levelSlug].average + "\t" + data[levelSlug].count + "\t" + levelSlug);
total += data[levelSlug];
}
// print(parseInt(total/60/60) + "\t\t total hours");
// break;
}
@ -79,32 +55,32 @@ function objectIdWithTimestamp(timestamp) {
return constructedObjectId
}
function getCampaigns() {
function getCampaigns(campaignSlugs) {
var campaigns = [];
var cursor = db.campaigns.find({}, {slug: 1, levels: 1});
var cursor = db.campaigns.find({slug: {$in: campaignSlugs}}, {slug: 1, levels: 1});
var allFree = 0;
var allpaid = 0;
while (cursor.hasNext()) {
var doc = cursor.next();
if (doc.slug === 'auditions') continue;
var campaign = {slug: doc.slug, free: [], paid: [], replayable: []};
var campaign = {slug: doc.slug, levelSlugs: []};
for (var levelID in doc.levels) {
if (doc.levels[levelID].replayable) {
campaign.replayable.push(doc.levels[levelID].slug);
}
else if (doc.levels[levelID].requiresSubscription) {
campaign.paid.push(doc.levels[levelID].slug);
}
else {
campaign.free.push(doc.levels[levelID].slug);
}
campaign.levelSlugs.push(doc.levels[levelID].slug);
}
campaigns.push(campaign);
}
campaigns.sort(function (a, b) {
if (campaignSlugs.indexOf(a.slug) < campaignSlugs.indexOf(b.slug)){
return -1;
}
return 1;
});
return campaigns;
}
function getPlaytimes(levelSlugs) {
// printjson(levelSlugs);
var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z"));
var endObj = objectIdWithTimestamp(ISODate(endDay + "T00:00:00.000Z"))
var cursor = db['level.sessions'].find({
@ -116,21 +92,49 @@ function getPlaytimes(levelSlugs) {
{_id: {$gte: startObj}},
{_id: {$lt: endObj}}
]
});
}, {heroConfig: 1, levelID: 1, playtime: 1});
var playtimes = {};
while (cursor.hasNext()) {
var myDoc = cursor.next();
var levelID = myDoc.levelID;
if (!playtimes[levelID]) playtimes[levelID] = [];
playtimes[levelID].push(myDoc.playtime);
if (!playtimes[levelID]) playtimes[levelID] = {campaign: [], course: []};
if (myDoc.heroConfig) {
playtimes[levelID].campaign.push(myDoc.playtime);
}
else {
playtimes[levelID].course.push(myDoc.playtime);
}
}
// printjson(playtimes);
var data = {};
for (levelID in playtimes) {
var total = playtimes[levelID].reduce(function(a, b) {return a + b;});
data[levelID] = {count: playtimes[levelID].length, total: total};
data[levelID]['average'] = parseInt(total / playtimes[levelID].length);
var campaignTotal = 0;
var courseTotal = 0;
if (playtimes[levelID].campaign.length > 0) {
campaignTotal = playtimes[levelID].campaign.reduce(function(a, b) {return a + b;});
}
if (playtimes[levelID].course.length > 0) {
courseTotal = playtimes[levelID].course.reduce(function(a, b) {return a + b;});
}
var campaignAverage = parseInt(playtimes[levelID].campaign.length > 0 ? parseInt(campaignTotal / playtimes[levelID].campaign.length): 0);
var courseAverage = parseInt(playtimes[levelID].course.length > 0 ? parseInt(courseTotal / playtimes[levelID].course.length): 0);
data[levelID] = {
campaign: {
count: playtimes[levelID].campaign.length,
total: campaignTotal,
average: campaignAverage
},
course: {
count: playtimes[levelID].course.length,
total: courseTotal,
average: courseAverage
}
};
}
return data;
}

View file

@ -0,0 +1,106 @@
// Campaign completion counts
// Usage:
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
var courseCampaigns = ['intro', 'course-2', 'course-3', 'course-4'];
var individualCampaigns = ['dungeon', 'forest', 'desert', 'mountain'];
var scriptStartTime = new Date();
var startDay = '2015-11-08';
var endDay = '2015-11-15'; // Not inclusive
log("Dates: " + startDay + " to " + endDay);
var campaigns = getCampaigns(courseCampaigns);
// var campaigns = getCampaigns(individualCampaigns);
// printjson(campaigns);
for (var i = 0; i < campaigns.length; i++) {
var campaign = campaigns[i];
print(campaign.slug);
print("Total\tCampaign\tCourse\tLevel");
var completionCounts = getCompletionCounts(campaign.levelSlugs);
for (var j = 0; j < campaign.levelSlugs.length; j++) {
var levelSlug = campaign.levelSlugs[j];
if (completionCounts[levelSlug]) {
print(completionCounts[levelSlug].campaign + completionCounts[levelSlug].course,
'\t', completionCounts[levelSlug].campaign,
'\t', completionCounts[levelSlug].course,
'\t', levelSlug);
}
else {
print(0, '\t', 0, '\t', 0, '\t', levelSlug);
}
}
}
log("Script runtime: " + (new Date() - scriptStartTime));
function log(str) {
print(new Date().toISOString() + " " + str);
}
function objectIdWithTimestamp(timestamp) {
// Convert string date to Date object (otherwise assume timestamp is a date)
if (typeof(timestamp) == 'string') timestamp = new Date(timestamp);
// Convert date object to hex seconds since Unix epoch
var hexSeconds = Math.floor(timestamp/1000).toString(16);
// Create an ObjectId with that hex timestamp
var constructedObjectId = ObjectId(hexSeconds + "0000000000000000");
return constructedObjectId
}
function getCampaigns(campaignSlugs) {
var campaigns = [];
var cursor = db.campaigns.find({slug: {$in: campaignSlugs}}, {slug: 1, levels: 1});
var allFree = 0;
var allpaid = 0;
while (cursor.hasNext()) {
var doc = cursor.next();
if (doc.slug === 'auditions') continue;
var campaign = {slug: doc.slug, levelSlugs: []};
for (var levelID in doc.levels) {
campaign.levelSlugs.push(doc.levels[levelID].slug);
}
campaigns.push(campaign);
}
campaigns.sort(function (a, b) {
if (campaignSlugs.indexOf(a.slug) < campaignSlugs.indexOf(b.slug)){
return -1;
}
return 1;
});
return campaigns;
}
function getCompletionCounts(levelSlugs) {
var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z"));
var endObj = objectIdWithTimestamp(ISODate(endDay + "T00:00:00.000Z"))
var cursor = db['level.sessions'].find({
$and:
[
{"state.complete": true},
{levelID: {$in: levelSlugs}},
{_id: {$gte: startObj}},
{_id: {$lt: endObj}}
]
}, {heroConfig: 1, levelID: 1});
var completionCounts = {};
while (cursor.hasNext()) {
var myDoc = cursor.next();
var levelID = myDoc.levelID;
if (!completionCounts[levelID]) completionCounts[levelID] = {campaign: 0, course: 0};
if (myDoc.heroConfig) {
completionCounts[levelID].campaign++;
}
else {
completionCounts[levelID].course++;
}
}
return completionCounts;
}

View file

@ -438,7 +438,6 @@ function getActiveUserCounts(startDay, activeUserEvents) {
if (!dayUserMap[day]) dayUserMap[day] = {};
dayUserMap[day][user] = true;
}
// printjson(dayUserMap['2015-11-01']);
var activeUsersCounts = {};
var monthlyActives = [];
@ -474,7 +473,8 @@ function getActiveClassCounts(startDay) {
'Active classes managed subscription': [],
'Active classes bulk subscription': [],
'Active classes prepaid': [],
'Active classes course': [],
'Active classes course free': [],
'Active classes course paid': []
};
var userPlayedMap = {};
@ -539,10 +539,10 @@ function getActiveClassCounts(startDay) {
}
}
// Prepaids terminal_subscription & course
// Prepaids terminal_subscription
bulkSubGroups = {};
cursor = db.prepaids.find(
{$and: [{type: {$in: ['terminal_subscription', 'course']}}, {$where: 'this.redeemers && this.redeemers.length >= ' + minGroupSize}]},
{$and: [{type: 'terminal_subscription'}, {$where: 'this.redeemers && this.redeemers.length >= ' + minGroupSize}]},
{creator: 1, type: 1, redeemers: 1}
);
while (cursor.hasNext()) {
@ -553,17 +553,37 @@ function getActiveClassCounts(startDay) {
userPlayedMap[doc.redeemers[i].userID.valueOf()] = [];
members.push(doc.redeemers[i].userID.valueOf());
}
var event = doc.type == 'terminal_subscription' ? 'Active classes prepaid' : 'Active classes course';
classes[event].push({
classes['Active classes prepaid'].push({
owner: owner,
members: members,
activeDayMap: {}
});
}
// printjson(classes);
// Classrooms
var classroomCourseInstancesMap = {};
cursor = db.course.instances.find(
{$where: 'this.members && this.members.length >= ' + minGroupSize},
{classroomID: 1, courseID: 1, members: 1, ownerID: 1}
);
while (cursor.hasNext()) {
var doc = cursor.next();
var owner = doc.ownerID.valueOf();
var classroom = doc.classroomID ? doc.classroomID.valueOf() : doc._id.valueOf();
var members = [];
for (var i = 0 ; i < doc.members.length; i++) {
userPlayedMap[doc.members[i].valueOf()] = [];
members.push(doc.members[i].valueOf());
}
if (!classroomCourseInstancesMap[classroom]) classroomCourseInstancesMap[classroom] = [];
classroomCourseInstancesMap[classroom].push({
course: doc.courseID.valueOf(),
owner: owner,
members: members,
});
}
// TODO: classrooms
// printjson(classroomCourseInstancesMap);
// Find all the started level events for our class members, for startDay - daysInMonth
var startDate = ISODate(startDay + "T00:00:00.000Z");
@ -573,11 +593,10 @@ function getActiveClassCounts(startDay) {
var startObj = objectIdWithTimestamp(startDate);
var queryParams = {$and: [
{_id: {$gte: startObj}},
{event: 'Started Level'},
{user: {$in: Object.keys(userPlayedMap)}}
{user: {$in: Object.keys(userPlayedMap)}},
{event: 'Started Level'}
]};
cursor = logDB['log'].find(queryParams, {user: 1});
// cursor = db['level.sessions'].find({$and: [{creator: {$in: Object.keys(userPlayedMap)}}, {changed: {$gte: startDate}}]}, {creator: 1, changed: 1});
while (cursor.hasNext()) {
var doc = cursor.next();
userPlayedMap[doc.user].push(doc._id.getTimestamp());
@ -621,6 +640,108 @@ function getActiveClassCounts(startDay) {
endDate.setUTCDate(endDate.getUTCDate() + 1);
}
// Classrooms are processed differently because they could be free or paid active classes
var courseNameMap = {};
cursor = db.courses.find({}, {name: 1});
while (cursor.hasNext()) {
var doc = cursor.next();
courseNameMap[doc._id.valueOf()] = doc.name;
}
// For each classroom, check free and paid members separately
for (var classroom in classroomCourseInstancesMap) {
var freeMembers = {};
var paidMembers = {};
var owner = null;
for (var i = 0; i < classroomCourseInstancesMap[classroom].length; i++) {
var courseInstance = classroomCourseInstancesMap[classroom][i];
if (!owner) owner = courseInstance.owner;
for (var j = 0; j < courseInstance.members.length; j++) {
if (courseNameMap[courseInstance.course] === 'Introduction to Computer Science') {
freeMembers[courseInstance.members[j]] = true;
}
else {
paidMembers[courseInstance.members[j]] = true;
}
}
}
var freeClass = {
owner: owner,
members: Object.keys(freeMembers),
activeDayMap: {}
};
var paidClass = {
owner: owner,
members: Object.keys(paidMembers),
activeDayMap: {}
};
// print('Processing classroom', classroom, freeClass.members.length, paidClass.members.length);
startDate = ISODate(startDay + "T00:00:00.000Z");
startDate.setUTCDate(startDate.getUTCDate() - daysInMonth);
endDate = ISODate(startDay + "T00:00:00.000Z");
while (endDate < todayDate) {
var endDay = endDate.toISOString().substring(0, 10);
// For each paid member of current class
var paidActiveMemberCount = 0;
for (var j = 0; j < paidClass.members.length; j++) {
var member = paidClass.members[j];
// Was member active during current timeframe?
if (userPlayedMap[member]) {
for (var k = 0; k < userPlayedMap[member].length; k++) {
if (userPlayedMap[member][k] > startDate && userPlayedMap[member][k] <= endDate) {
paidActiveMemberCount++;
break;
}
}
}
}
// Classes active for a given day if has minGroupSize members, and at least 1/2 played in last daysInMonth days
if (paidClass.members.length > minGroupSize && paidActiveMemberCount >= Math.round(paidClass.members.length / 2)) {
// print('paid classroom', classroom, endDay);
paidClass.activeDayMap[endDay] = true;
}
else {
// For each free member of current class
var freeActiveMemberCount = 0;
for (var j = 0; j < freeClass.members.length; j++) {
var member = freeClass.members[j];
// Was member active during current timeframe?
if (userPlayedMap[member]) {
for (var k = 0; k < userPlayedMap[member].length; k++) {
if (userPlayedMap[member][k] > startDate && userPlayedMap[member][k] <= endDate) {
freeActiveMemberCount++;
break;
}
}
}
}
if (freeClass.members.length > minGroupSize && freeActiveMemberCount >= Math.round(freeClass.members.length / 2)) {
// print('free classroom', classroom, endDay);
freeClass.activeDayMap[endDay] = true;
}
}
startDate.setUTCDate(startDate.getUTCDate() + 1);
endDate.setUTCDate(endDate.getUTCDate() + 1);
}
// printjson(freeClass);
// printjson(paidClass);
classes['Active classes course free'].push(freeClass);
classes['Active classes course paid'].push(paidClass);
}
// printjson(classes['Active classes course paid']);
var activeClassCounts = {};
for (var event in classes) {
if (!activeClassCounts[event]) activeClassCounts[event] = {};

View file

@ -138,11 +138,11 @@ findUserToSchool = (users) ->
for suggestion in suggestions
for reason in suggestion.reasons
sum += switch reason
when 'Course instances' then 50
when 'Course instances' then 150
when 'IP' then 40
when 'Name' then 30
when 'Referrer' then 20
when 'Domain' then (if getDomain(target) is 'cps.edu' then 1 else 10)
when 'Name' then 15
when 'Domain' then (if getDomain(target) in ['cps.edu', 'mynewcaneyisd.org', 'fsusd.org'] then 1 else 10)
when 'Clans' then 0.01
sum
), 0
@ -205,7 +205,7 @@ findSuggestions = (target) ->
else
suggestions.push schoolName: otherUser.schoolName, reasons: [reason], user: otherUser
if debugging then console.log ' Done checking referrer', (new Date()) - t0
suggestions = _.sortBy suggestions, 'schoolName'
suggestions = _.sortBy suggestions, (s) -> (s.schoolName or '').toLowerCase()
suggestions = _.sortBy suggestions, (s) -> -s.reasons.length
return suggestions
@ -215,7 +215,8 @@ usersCategorized = {}
sortUsers = (users) ->
users = _.sortBy users, (u) -> -u.points
users = _.sortBy users, ['schoolName', 'lastIP']
users = _.sortBy users, 'lastIP'
users = _.sortBy users, (u) -> (u.schoolName or '').toLowerCase()
for field in ['courseInstances', 'lastIP', 'schoolName', 'domain', 'clans', 'referrer']
userCategories[field] = categorizeUsers users, field
topGroups[field] = _.sortBy _.keys(userCategories[field]), (key) -> -userCategories[field][key].length

View file

@ -30,7 +30,9 @@ class AnalyticsPerDayHandler extends Handler
'Active classes managed subscription',
'Active classes bulk subscription',
'Active classes prepaid',
'Active classes course']
'Active classes course free',
'Active classes course paid'
]
AnalyticsString.find({v: {$in: events}}).exec (err, documents) =>
return @sendDatabaseError(res, err) if err

View file

@ -192,6 +192,9 @@ module.exports = class Handler
hasLimit = false
try
for own key, val of req.query.conditions
numeric = parseInt val, 10
if not _.isNaN(numeric) and numeric + '' is val
val = numeric
query = query[key](val)
hasLimit ||= key is 'limit'
catch e

View file

@ -51,7 +51,6 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
if courseInstance
console.log 'already made a course instance'
return @sendSuccess(res, courseInstance) if courseInstance
console.log 'making a new course instance'
courseInstance = new CourseInstance({
courseID: courseID
members: [req.user.get('_id')]

View file

@ -0,0 +1,5 @@
mongoose = require('mongoose')
config = require '../../server_config'
MandateSchema = new mongoose.Schema {}, {strict: false, read: config.mongo.readpref}
module.exports = mongoose.model('mandate', MandateSchema)

View file

@ -0,0 +1,5 @@
mongoose = require('mongoose')
config = require '../../server_config'
ProductSchema = new mongoose.Schema({}, {strict: false,read:config.mongo.readpref})
module.exports = mongoose.model('product', ProductSchema)

View file

@ -1,4 +1,5 @@
Payment = require './Payment'
Product = require '../models/Product'
User = require '../users/User'
Handler = require '../commons/Handler'
{handlers} = require '../commons/mapping'
@ -11,30 +12,6 @@ request = require 'request'
async = require 'async'
apple_utils = require '../lib/apple_utils'
products = {
'gems_5': {
amount: 499
gems: 5000
id: 'gems_5'
}
'gems_10': {
amount: 999
gems: 11000
id: 'gems_10'
}
'gems_20': {
amount: 1999
gems: 25000
id: 'gems_20'
}
'custom': {
# amount expected in request body
id: 'custom'
}
}
PaymentHandler = class PaymentHandler extends Handler
modelClass: Payment
@ -134,33 +111,34 @@ PaymentHandler = class PaymentHandler extends Handler
payment = @makeNewInstance(req)
payment.set 'service', 'ios'
product = products[transaction.product_id]
payment.set 'amount', product.amount
payment.set 'gems', product.gems
payment.set 'ios', {
transactionID: transactionID
rawReceipt: receipt
localPrice: localPrice
}
validation = @validateDocumentInput(payment.toObject())
if validation.valid is false
@logPaymentError(req, 'Invalid apple payment object.')
return @sendBadInputError(res, validation.errors)
payment.save((err) =>
if err
@logPaymentError(req, 'Apple payment save error.'+err)
return @sendDatabaseError(res, err)
@incrementGemsFor(req.user, product.gems, (err) =>
Product.findOne({name: transaction.product_id}).exec (err, product) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) if not product
payment.set 'amount', product.get('amount')
payment.set 'gems', product.get('gems')
payment.set 'ios', {
transactionID: transactionID
rawReceipt: receipt
localPrice: localPrice
}
validation = @validateDocumentInput(payment.toObject())
if validation.valid is false
@logPaymentError(req, 'Invalid apple payment object.')
return @sendBadInputError(res, validation.errors)
payment.save((err) =>
if err
@logPaymentError(req, 'Apple incr db error.'+err)
@logPaymentError(req, 'Apple payment save error.'+err)
return @sendDatabaseError(res, err)
@sendPaymentHipChatMessage user: req.user, payment: payment
@sendCreated(res, @formatEntity(req, payment))
@incrementGemsFor(req.user, product.get('gems'), (err) =>
if err
@logPaymentError(req, 'Apple incr db error.'+err)
return @sendDatabaseError(res, err)
@sendPaymentHipChatMessage user: req.user, payment: payment
@sendCreated(res, @formatEntity(req, payment))
)
)
)
)
)
@ -203,7 +181,6 @@ PaymentHandler = class PaymentHandler extends Handler
beginStripePayment: (req, res, timestamp, productID) ->
product = products[productID]
async.parallel([
((callback) ->
@ -218,6 +195,10 @@ PaymentHandler = class PaymentHandler extends Handler
charge = _.find recentCharges.data, (c) -> c.metadata.timestamp is timestamp
callback(null, charge)
)
),
((callback) ->
Product.findOne({name: productID}).exec (err, product) =>
callback(err, product)
)
],
@ -225,7 +206,10 @@ PaymentHandler = class PaymentHandler extends Handler
if err
@logPaymentError(req, 'Stripe async load db error. '+err)
return @sendDatabaseError(res, err)
[payment, charge] = results
[payment, charge, product] = results
if not product
return @sendNotFoundError(res, 'could not find product with id '+productID)
if not (payment or charge)
# Proceed normally from the beginning
@ -236,7 +220,7 @@ PaymentHandler = class PaymentHandler extends Handler
@recordStripeCharge(req, res, charge)
else
return @sendSuccess(res, @formatEntity(req, payment)) if product.id is 'custom'
return @sendSuccess(res, @formatEntity(req, payment)) if product.get('name') is 'custom'
# Charged Stripe and recorded it. Recalculate gems to make sure credited the purchase.
@recalculateGemsFor(req.user, (err) =>
@ -250,7 +234,7 @@ PaymentHandler = class PaymentHandler extends Handler
)
chargeStripe: (req, res, product) ->
amount = parseInt product.amount ? req.body.amount
amount = parseInt product.get('amount') ? req.body.amount
return @sendError(res, 400, "Invalid amount.") if isNaN(amount)
stripe.charges.create({
@ -258,9 +242,9 @@ PaymentHandler = class PaymentHandler extends Handler
currency: 'usd'
customer: req.user.get('stripe')?.customerID
metadata: {
productID: product.id
productID: product.get('name')
userID: req.user._id + ''
gems: product.gems
gems: product.get('gems')
timestamp: parseInt(req.body.stripe?.timestamp)
description: req.body.description
}

View file

@ -15,20 +15,10 @@ User = require '../users/User'
{getSponsoredSubsAmount} = require '../../app/core/utils'
StripeUtils = require '../lib/stripe_utils'
moment = require 'moment'
Product = require '../models/Product'
recipientCouponID = 'free'
# TODO: rename this to avoid collisions with 'subscriptions' variables
subscriptions = {
basic: {
gems: 3500
amount: 999 # For calculating incremental quantity before sub creation
}
year_sale: {
amount: 9900
}
}
class SubscriptionHandler extends Handler
logSubscriptionError: (user, msg) ->
console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'"
@ -149,51 +139,56 @@ class SubscriptionHandler extends Handler
if err
@logSubscriptionError(user, "Purchase year sale Stripe cancel subscription error: #{JSON.stringify(err)}")
return @sendDatabaseError(res, err)
metadata =
type: req.body.type
userID: req.user._id + ''
gems: subscriptions.basic.gems * 12
timestamp: parseInt(req.body.stripe?.timestamp)
description: req.body.description
StripeUtils.createCharge req.user, subscriptions.year_sale.amount, metadata, (err, charge) =>
if err
@logSubscriptionError(req.user, "Purchase year sale create charge: #{JSON.stringify(err)}")
return @sendDatabaseError(res, err)
StripeUtils.createPayment req.user, charge, (err, payment) =>
Product.findOne({name: 'year_subscription'}).exec (err, product) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res, 'year_subscription product not found') if not product
metadata =
type: req.body.type
userID: req.user._id + ''
gems: product.get('gems')
timestamp: parseInt(req.body.stripe?.timestamp)
description: req.body.description
StripeUtils.createCharge req.user, product.get('amount'), metadata, (err, charge) =>
if err
@logSubscriptionError(req.user, "Purchase year sale create payment: #{JSON.stringify(err)}")
@logSubscriptionError(req.user, "Purchase year sale create charge: #{JSON.stringify(err)}")
return @sendDatabaseError(res, err)
# Add terminal subscription to User with extensions for existing subscriptions
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
endDate = new Date()
if stripeSubscriptionPeriodEndDate
endDate = stripeSubscriptionPeriodEndDate
else if _.isString(stripeInfo.free) and new Date() < new Date(stripeInfo.free)
endDate = new Date(stripeInfo.free)
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
stripeInfo.free = endDate.toISOString().substring(0, 10)
req.user.set('stripe', stripeInfo)
# Add year's worth of gems to User
purchased = _.clone(req.user.get('purchased'))
purchased ?= {}
purchased.gems ?= 0
purchased.gems += parseInt(charge.metadata.gems)
req.user.set('purchased', purchased)
req.user.save (err, user) =>
StripeUtils.createPayment req.user, charge, (err, payment) =>
if err
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
@logSubscriptionError(req.user, "Purchase year sale create payment: #{JSON.stringify(err)}")
return @sendDatabaseError(res, err)
try
msg = "Year subscription purchased by #{req.user.get('email')} #{req.user.id}"
hipchat.sendHipChatMessage msg, ['tower']
catch error
@logSubscriptionError(req.user, "Year sub sale HipChat tower msg error: #{JSON.stringify(error)}")
@sendSuccess(res, user)
# Add terminal subscription to User with extensions for existing subscriptions
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
endDate = new Date()
if stripeSubscriptionPeriodEndDate
endDate = stripeSubscriptionPeriodEndDate
else if _.isString(stripeInfo.free) and new Date() < new Date(stripeInfo.free)
endDate = new Date(stripeInfo.free)
endDate.setUTCFullYear(endDate.getUTCFullYear() + 1)
stripeInfo.free = endDate.toISOString().substring(0, 10)
req.user.set('stripe', stripeInfo)
# Add year's worth of gems to User
purchased = _.clone(req.user.get('purchased'))
purchased ?= {}
purchased.gems ?= 0
purchased.gems += parseInt(charge.metadata.gems)
req.user.set('purchased', purchased)
req.user.save (err, user) =>
if err
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
return @sendDatabaseError(res, err)
try
msg = "Year subscription purchased by #{req.user.get('email')} #{req.user.id}"
hipchat.sendHipChatMessage msg, ['tower']
catch error
@logSubscriptionError(req.user, "Year sub sale HipChat tower msg error: #{JSON.stringify(error)}")
@sendSuccess(res, user)
subscribeWithPrepaidCode: (req, res) ->
return @sendUnauthorizedError(res) unless req.user?
@ -241,31 +236,35 @@ class SubscriptionHandler extends Handler
@logSubscriptionError(user, "Redeem Prepaid Code Stripe cancel subscription error: #{JSON.stringify(err)}")
return @sendDatabaseError(res, err)
# Add terminal subscription to User, extending existing subscriptions
# TODO: refactor this into some form useable by both this and purchaseYearSale
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
endDate = new moment()
if stripeSubscriptionPeriodEndDate
endDate = new moment(stripeSubscriptionPeriodEndDate)
else if _.isString(stripeInfo.free) and new moment().isBefore(new moment(stripeInfo.free))
endDate = new moment(stripeInfo.free)
Product.findOne({name: 'basic_subscription'}).exec (err, product) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res, 'basic_subscription product not found') if not product
endDate = endDate.add(months, 'months')
stripeInfo.free = endDate.toISOString().substring(0, 10)
req.user.set('stripe', stripeInfo)
# Add gems to User
purchased = _.clone(req.user.get('purchased'))
purchased ?= {}
purchased.gems ?= 0
purchased.gems += subscriptions.basic.gems * months
req.user.set('purchased', purchased)
req.user.save (err, user) =>
if err
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
return @sendDatabaseError(res, err)
@sendSuccess(res, user)
# Add terminal subscription to User, extending existing subscriptions
# TODO: refactor this into some form useable by both this and purchaseYearSale
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
endDate = new moment()
if stripeSubscriptionPeriodEndDate
endDate = new moment(stripeSubscriptionPeriodEndDate)
else if _.isString(stripeInfo.free) and new moment().isBefore(new moment(stripeInfo.free))
endDate = new moment(stripeInfo.free)
endDate = endDate.add(months, 'months')
stripeInfo.free = endDate.toISOString().substring(0, 10)
req.user.set('stripe', stripeInfo)
# Add gems to User
purchased = _.clone(req.user.get('purchased'))
purchased ?= {}
purchased.gems ?= 0
purchased.gems += product.get('gems') * months
req.user.set('purchased', purchased)
req.user.save (err, user) =>
if err
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
return @sendDatabaseError(res, err)
@sendSuccess(res, user)
subscribeUser: (req, user, done) ->
if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
@ -427,18 +426,22 @@ class SubscriptionHandler extends Handler
req.body.stripe = stripeInfo
user.set('stripe', stripeInfo)
if increment
purchased = _.clone(user.get('purchased'))
purchased ?= {}
purchased.gems ?= 0
purchased.gems += subscriptions.basic.gems # TODO: Put actual subscription amount here
user.set('purchased', purchased)
Product.findOne({name: 'basic_subscription'}).exec (err, product) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res, 'basic_subscription product not found') if not product
user.save (err) =>
if err
@logSubscriptionError(user, 'Stripe user plan saving error. ' + err)
return done({res: 'Database error.', code: 500})
done()
if increment
purchased = _.clone(user.get('purchased'))
purchased ?= {}
purchased.gems ?= 0
purchased.gems += product.get('gems') # TODO: Put actual subscription amount here
user.set('purchased', purchased)
user.save (err) =>
if err
@logSubscriptionError(user, 'Stripe user plan saving error. ' + err)
return done({res: 'Database error.', code: 500})
done()
updateStripeRecipientSubscriptions: (req, user, customer, done) ->
return done({res: 'Database error.', code: 500}) unless req.body.stripe?.subscribeEmails?
@ -527,36 +530,40 @@ class SubscriptionHandler extends Handler
@logSubscriptionError(user, 'User saving stripe error. ' + err)
return done({res: 'Database error.', code: 500})
createUpdateFn = (recipient, increment) =>
(done) =>
# Update recipient
stripeInfo = _.cloneDeep(recipient.get('stripe') ? {})
stripeInfo.sponsorID = user.id
recipient.set 'stripe', stripeInfo
if increment
purchased = _.clone(recipient.get('purchased'))
purchased ?= {}
purchased.gems ?= 0
purchased.gems += subscriptions.basic.gems
recipient.set('purchased', purchased)
recipient.save (err) =>
if err
@logSubscriptionError(user, 'Stripe user saving stripe error. ' + err)
return done({res: 'Database error.', code: 500})
done()
Product.findOne({name: 'basic_subscription'}).exec (err, product) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res, 'basic_subscription product not found') if not product
tasks = []
for sub in stripeRecipients
tasks.push createUpdateFn(sub.recipient, sub.increment)
createUpdateFn = (recipient, increment) =>
(done) =>
# Update recipient
stripeInfo = _.cloneDeep(recipient.get('stripe') ? {})
stripeInfo.sponsorID = user.id
recipient.set 'stripe', stripeInfo
if increment
purchased = _.clone(recipient.get('purchased'))
purchased ?= {}
purchased.gems ?= 0
purchased.gems += product.get('gems')
recipient.set('purchased', purchased)
recipient.save (err) =>
if err
@logSubscriptionError(user, 'Stripe user saving stripe error. ' + err)
return done({res: 'Database error.', code: 500})
done()
tasks = []
for sub in stripeRecipients
tasks.push createUpdateFn(sub.recipient, sub.increment)
async.parallel tasks, (err, results) =>
return done(err) if err
@updateStripeSponsorSubscription(req, user, customer, product, done)
async.parallel tasks, (err, results) =>
return done(err) if err
@updateStripeSponsorSubscription(req, user, customer, done)
updateStripeSponsorSubscription: (req, user, customer, done) ->
updateStripeSponsorSubscription: (req, user, customer, product, done) ->
stripeInfo = user.get('stripe') ? {}
numSponsored = stripeInfo.recipients.length
quantity = getSponsoredSubsAmount(subscriptions.basic.amount, numSponsored, stripeInfo.subscriptionID?)
quantity = getSponsoredSubsAmount(product.get('amount'), numSponsored, stripeInfo.subscriptionID?)
findStripeSubscription customer.id, subscriptionID: stripeInfo.sponsorSubscriptionID, (subscription) =>
if stripeInfo.sponsorSubscriptionID? and not subscription?
@ -656,38 +663,42 @@ class SubscriptionHandler extends Handler
@logSubscriptionError(user, 'Unable to find recipient subscription. ')
return done({res: 'Database error.', code: 500})
# Update recipient user
deleteUserStripeProp(recipient, 'sponsorID')
recipient.save (err) =>
if err
@logSubscriptionError(user, 'Recipient user save unsubscribe error. ' + err)
return done({res: 'Database error.', code: 500})
Product.findOne({name: 'basic_subscription'}).exec (err, product) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res, 'basic_subscription product not found') if not product
# Cancel Stripe subscription
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, (err) =>
# Update recipient user
deleteUserStripeProp(recipient, 'sponsorID')
recipient.save (err) =>
if err
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err)
@logSubscriptionError(user, 'Recipient user save unsubscribe error. ' + err)
return done({res: 'Database error.', code: 500})
# Update sponsor user
_.remove(stripeInfo.recipients, (s) -> s.userID is recipient.id)
delete stripeInfo.unsubscribeEmail
user.set('stripe', stripeInfo)
req.body.stripe = stripeInfo
user.save (err) =>
# Cancel Stripe subscription
stripe.customers.cancelSubscription stripeInfo.customerID, sponsoredEntry.subscriptionID, (err) =>
if err
@logSubscriptionError(user, 'Sponsor user save unsubscribe error. ' + err)
@logSubscriptionError(user, "Stripe cancel sponsored subscription failed. " + err)
return done({res: 'Database error.', code: 500})
return done() unless stripeInfo.sponsorSubscriptionID?
# Update sponsored subscription quantity
options =
quantity: getSponsoredSubsAmount(subscriptions.basic.amount, stripeInfo.recipients.length, stripeInfo.subscriptionID?)
stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) =>
# Update sponsor user
_.remove(stripeInfo.recipients, (s) -> s.userID is recipient.id)
delete stripeInfo.unsubscribeEmail
user.set('stripe', stripeInfo)
req.body.stripe = stripeInfo
user.save (err) =>
if err
@logSubscriptionError(user, 'Sponsored subscription quantity update error. ' + JSON.stringify(err))
@logSubscriptionError(user, 'Sponsor user save unsubscribe error. ' + err)
return done({res: 'Database error.', code: 500})
done()
return done() unless stripeInfo.sponsorSubscriptionID?
# Update sponsored subscription quantity
options =
quantity: getSponsoredSubsAmount(product.get('amount'), stripeInfo.recipients.length, stripeInfo.subscriptionID?)
stripe.customers.updateSubscription stripeInfo.customerID, stripeInfo.sponsorSubscriptionID, options, (err, subscription) =>
if err
@logSubscriptionError(user, 'Sponsored subscription quantity update error. ' + JSON.stringify(err))
return done({res: 'Database error.', code: 500})
done()
module.exports = new SubscriptionHandler()

View file

@ -6,6 +6,7 @@ User = require '../users/User'
StripeUtils = require '../lib/stripe_utils'
utils = require '../../app/core/utils'
mongoose = require 'mongoose'
Product = require '../models/Product'
# TODO: Should this happen on a save() call instead of a prepaid/-/create post?
# TODO: Probably a better way to create a unique 8 charactor string property using db voodoo
@ -17,8 +18,6 @@ PrepaidHandler = class PrepaidHandler extends Handler
jsonSchema: require '../../app/schemas/models/prepaid.schema'
allowedMethods: ['GET','POST']
baseAmount: 999
logError: (user, msg) ->
console.warn "Prepaid Error: [#{user.get('slug')} (#{user._id})] '#{msg}'"
@ -134,9 +133,13 @@ PrepaidHandler = class PrepaidHandler extends Handler
return @sendBadInputError(res) unless isNaN(months) is false and months > 0
return @sendError(res, 403, "Users or Months must be greater than 3") if maxRedeemers < 3 and months < 3
@purchasePrepaidTerminalSubscription req.user, description, maxRedeemers, months, timestamp, token, (err, prepaid) =>
Product.findOne({name: 'prepaid_subscription'}).exec (err, product) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, prepaid.toObject())
return @sendNotFoundError(res, 'prepaid_subscription product not found') if not product
@purchasePrepaidTerminalSubscription req.user, description, maxRedeemers, months, timestamp, token, product, (err, prepaid) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, prepaid.toObject())
else if req.body.type is 'course'
maxRedeemers = parseInt(req.body.maxRedeemers)
@ -145,18 +148,22 @@ PrepaidHandler = class PrepaidHandler extends Handler
return @sendBadInputError(res) unless isNaN(maxRedeemers) is false and maxRedeemers > 0
@purchasePrepaidCourse req.user, maxRedeemers, timestamp, token, (err, prepaid) =>
# TODO: this badinput detection is fragile, in course instance handler as well
return @sendBadInputError(res, err) if err is 'Missing required Stripe token'
Product.findOne({name: 'course'}).exec (err, product) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, prepaid.toObject())
return @sendNotFoundError(res, 'course product not found') if not product
@purchasePrepaidCourse req.user, maxRedeemers, timestamp, token, product, (err, prepaid) =>
# TODO: this badinput detection is fragile, in course instance handler as well
return @sendBadInputError(res, err) if err is 'Missing required Stripe token'
return @sendDatabaseError(res, err) if err
@sendSuccess(res, prepaid.toObject())
else
@sendForbiddenError(res)
purchasePrepaidCourse: (user, maxRedeemers, timestamp, token, done) ->
purchasePrepaidCourse: (user, maxRedeemers, timestamp, token, product, done) ->
type = 'course'
amount = maxRedeemers * 400
amount = maxRedeemers * product.get('amount')
if amount > 0 and not (token or user.isAdmin())
@logError(user, "Purchase prepaid courses missing required Stripe token #{amount}")
return done('Missing required Stripe token')
@ -190,7 +197,7 @@ PrepaidHandler = class PrepaidHandler extends Handler
hipchat.sendHipChatMessage msg, ['tower']
@createPrepaid(user, type, maxRedeemers, {}, done)
purchasePrepaidTerminalSubscription: (user, description, maxRedeemers, months, timestamp, token, done) ->
purchasePrepaidTerminalSubscription: (user, description, maxRedeemers, months, timestamp, token, product, done) ->
type = 'terminal_subscription'
StripeUtils.getCustomer user, token, (err, customer) =>
@ -207,7 +214,7 @@ PrepaidHandler = class PrepaidHandler extends Handler
months: months
productID: "prepaid #{type}"
amount = utils.getPrepaidCodeAmount(@baseAmount, maxRedeemers, months)
amount = utils.getPrepaidCodeAmount(product.get('amount'), maxRedeemers, months)
StripeUtils.createCharge user, amount, metadata, (err, charge) =>
if err

View file

@ -3,6 +3,7 @@ async = require 'async'
errors = require '../../commons/errors'
scoringUtils = require './scoringUtils'
LevelSession = require '../../levels/sessions/LevelSession'
Mandate = require '../../models/Mandate'
module.exports = getTwoGames = (req, res) ->
#return errors.unauthorized(res, 'You need to be logged in to get games.') unless req.user?.get('email')
@ -10,11 +11,15 @@ module.exports = getTwoGames = (req, res) ->
humansSessionID = req.body.humansGameID
ogresSessionID = req.body.ogresGameID
return getSpecificSessions res, humansSessionID, ogresSessionID if humansSessionID and ogresSessionID
options =
background: req.body.background
levelID: req.body.levelID
leagueID: req.body.leagueID
getRandomSessions req.user, options, sendSessionsResponse(res)
Mandate.findOne({}).cache(5 * 60 * 1000).exec (err, mandate) ->
if err then return errors.serverError res, "Error fetching our Mandate: #{err}"
if (throughputRatio = mandate?.get 'simulationThroughputRatio')? and Math.random() > throughputRatio
return sendSessionsResponse(res)(null, [])
options =
background: req.body.background
levelID: req.body.levelID
leagueID: req.body.leagueID
getRandomSessions req.user, options, sendSessionsResponse(res)
sessionSelectionString = 'team totalScore transpiledCode submittedCodeLanguage teamSpells levelID creatorName creator submitDate leagues'
@ -22,7 +27,6 @@ sendSessionsResponse = (res) ->
(err, sessions) ->
if err then return errors.serverError res, "Couldn't get two games to simulate: #{err}"
unless _.filter(sessions).length is 2
console.log 'No games to score.', sessions.length
res.send 204, 'No games to score.'
return res.end()
taskObject = messageGenerated: Date.now(), sessions: (scoringUtils.formatSessionInformation session for session in sessions)

View file

@ -0,0 +1,79 @@
Product = require '../../models/Product'
errors = require '../../commons/errors'
config = require '../../../server_config'
module.exports.get = (req, res) ->
Product.find().lean().exec (err, products) ->
return errors.serverError(res) if err
names = (product.name for product in products)
unless config.isProduction
for product in initProducts
if not _.contains(names, product.name)
# upsert products in initProducts if they DNE
products.push(product)
new Product(product).save _.noop
res.send(products)
###
Stub data, used in tests and dev environment.
These values are only upserted when the test/dev db does not already contain them.
If you are testing products and need to change them, you'll need to edit the db values directly.
###
initProducts = [
{
name: 'gems_5'
amount: 100
gems: 5000
priceString: '$1.00'
i18n: 'buy_gems.few_gems'
}
{
name: 'gems_10'
amount: 101
gems: 11000
priceString: '$1.01'
i18n: 'buy_gems.pile_gems'
}
{
name: 'gems_20'
amount: 102
gems: 25000
priceString: '$1.02'
i18n: 'buy_gems.chest_gems'
}
{
name: 'custom'
type: 'purchase'
}
{
name: 'basic_subscription'
amount: 100
gems: 3500
planID: 'basic'
}
{
name: 'year_subscription'
amount: 1000
gems: 42000
}
{
name: 'prepaid_subscription'
amount: 100
gems: 3500
}
{
name: 'course'
amount: 100
}
]

View file

@ -0,0 +1,2 @@
module.exports.setup = (app) ->
app.get('/db/products', require('./db/product').get)

View file

@ -14,6 +14,7 @@ user = require './server/users/user_handler'
logging = require './server/commons/logging'
config = require './server_config'
auth = require './server/routes/auth'
routes = require './server/routes'
UserHandler = require './server/users/user_handler'
hipchat = require './server/hipchat'
global.tv4 = require 'tv4' # required for TreemaUtils to work
@ -29,6 +30,7 @@ productionLogging = (tokens, req, res) ->
else if status >= 300 then color = 36
elapsed = (new Date()) - req._startTime
elapsedColor = if elapsed < 500 then 90 else 31
return null if status is 404 and /\/feedback/.test req.originalUrl # We know that these usually 404 by design (bad design?)
if (status isnt 200 and status isnt 201 and status isnt 204 and status isnt 304 and status isnt 302) or elapsed > 500
return "\x1b[90m#{req.method} #{req.originalUrl} \x1b[#{color}m#{res.statusCode} \x1b[#{elapsedColor}m#{elapsed}ms\x1b[0m"
null
@ -166,6 +168,7 @@ setupFacebookCrossDomainCommunicationRoute = (app) ->
res.sendfile path.join(__dirname, 'public', 'channel.html')
exports.setupRoutes = (app) ->
routes.setup(app)
app.use app.router
baseRoute.setup app

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCUnKaReE7xLUdVx6ixF1h",
"id": "tok_17IOOlKaReE7xLUdF9cOZScg",
"card": {
"id": "card_17GCUnKaReE7xLUdZ8FpRcR5"
"id": "card_17IOOlKaReE7xLUdwvPnfV15"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCuNWxO5eWsQG",
"id": "cus_7XTLw21mDCPduP",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCUnKaReE7xLUdZ8FpRcR5",
"customer": "cus_7VCuNWxO5eWsQG"
"id": "card_17IOOlKaReE7xLUdwvPnfV15",
"customer": "cus_7XTLw21mDCPduP"
}
],
"has_more": false
@ -43,55 +43,159 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuNWxO5eWsQG/subscriptions",
"path": "/v1/customers/cus_7XTLw21mDCPduP/subscriptions",
"status": 200,
"response": {
"id": "sub_7VCuZWrSt0bF8c",
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452363418,
"current_period_start": 1449685018,
"customer": "cus_7VCuNWxO5eWsQG",
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7VCuNWxO5eWsQG",
"subscription": "sub_7VCuZWrSt0bF8c"
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
},
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCuNWxO5eWsQG/subscriptions/sub_7VCuZWrSt0bF8c",
"method": "POST",
"path": "/v1/customers/cus_7XTLw21mDCPduP",
"status": 200,
"response": {
"id": "sub_7VCuZWrSt0bF8c",
"id": "cus_7XTLw21mDCPduP",
"alipay_accounts": {
"has_more": false
},
"email": "normal@jo.com",
"metadata": {
"id": "00000000000000000000005d"
},
"sources": {
"data": [
{
"id": "card_17IOOlKaReE7xLUdwvPnfV15",
"customer": "cus_7XTLw21mDCPduP"
}
],
"has_more": false
},
"subscriptions": {
"data": [
{
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 100
},
"quantity": 1
}
],
"has_more": false
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7XTLw21mDCPduP",
"status": 200,
"response": {
"id": "cus_7XTLw21mDCPduP",
"alipay_accounts": {
"has_more": false
},
"email": "normal@jo.com",
"metadata": {
"id": "00000000000000000000005d"
},
"sources": {
"data": [
{
"id": "card_17IOOlKaReE7xLUdwvPnfV15",
"customer": "cus_7XTLw21mDCPduP"
}
],
"has_more": false
},
"subscriptions": {
"data": [
{
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 100
},
"quantity": 1
}
],
"has_more": false
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7XTLw21mDCPduP/subscriptions/sub_7XTLVSO3Guyty9",
"status": 200,
"response": {
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452363418,
"current_period_start": 1449685018,
"customer": "cus_7VCuNWxO5eWsQG",
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7VCuNWxO5eWsQG",
"subscription": "sub_7VCuZWrSt0bF8c"
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}

View file

@ -11,10 +11,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuNWxO5eWsQG",
"path": "/v1/customers/cus_7XTLw21mDCPduP",
"status": 200,
"response": {
"id": "cus_7VCuNWxO5eWsQG",
"id": "cus_7XTLw21mDCPduP",
"alipay_accounts": {
"has_more": false
},
@ -22,7 +22,7 @@
"coupon": {
"id": "20pct"
},
"customer": "cus_7VCuNWxO5eWsQG"
"customer": "cus_7XTLw21mDCPduP"
},
"email": "normal@jo.com",
"metadata": {
@ -31,8 +31,8 @@
"sources": {
"data": [
{
"id": "card_17GCUnKaReE7xLUdZ8FpRcR5",
"customer": "cus_7VCuNWxO5eWsQG"
"id": "card_17IOOlKaReE7xLUdwvPnfV15",
"customer": "cus_7XTLw21mDCPduP"
}
],
"has_more": false
@ -40,24 +40,24 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCuZWrSt0bF8c",
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452363418,
"current_period_start": 1449685018,
"customer": "cus_7VCuNWxO5eWsQG",
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7VCuNWxO5eWsQG",
"subscription": "sub_7VCuZWrSt0bF8c"
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -69,10 +69,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCuNWxO5eWsQG",
"path": "/v1/customers/cus_7XTLw21mDCPduP",
"status": 200,
"response": {
"id": "cus_7VCuNWxO5eWsQG",
"id": "cus_7XTLw21mDCPduP",
"alipay_accounts": {
"has_more": false
},
@ -80,7 +80,7 @@
"coupon": {
"id": "20pct"
},
"customer": "cus_7VCuNWxO5eWsQG"
"customer": "cus_7XTLw21mDCPduP"
},
"email": "normal@jo.com",
"metadata": {
@ -89,8 +89,8 @@
"sources": {
"data": [
{
"id": "card_17GCUnKaReE7xLUdZ8FpRcR5",
"customer": "cus_7VCuNWxO5eWsQG"
"id": "card_17IOOlKaReE7xLUdwvPnfV15",
"customer": "cus_7XTLw21mDCPduP"
}
],
"has_more": false
@ -98,24 +98,24 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCuZWrSt0bF8c",
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452363418,
"current_period_start": 1449685018,
"customer": "cus_7VCuNWxO5eWsQG",
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7VCuNWxO5eWsQG",
"subscription": "sub_7VCuZWrSt0bF8c"
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}

View file

@ -2,17 +2,17 @@
{
"scope": "https://api.stripe.com:443",
"method": "DELETE",
"path": "/v1/customers/cus_7VCuNWxO5eWsQG/discount",
"path": "/v1/customers/cus_7XTLw21mDCPduP/discount",
"status": 200,
"response": {}
},
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCuNWxO5eWsQG",
"path": "/v1/customers/cus_7XTLw21mDCPduP",
"status": 200,
"response": {
"id": "cus_7VCuNWxO5eWsQG",
"id": "cus_7XTLw21mDCPduP",
"alipay_accounts": {
"has_more": false
},
@ -23,8 +23,8 @@
"sources": {
"data": [
{
"id": "card_17GCUnKaReE7xLUdZ8FpRcR5",
"customer": "cus_7VCuNWxO5eWsQG"
"id": "card_17IOOlKaReE7xLUdwvPnfV15",
"customer": "cus_7XTLw21mDCPduP"
}
],
"has_more": false
@ -32,24 +32,24 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCuZWrSt0bF8c",
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452363418,
"current_period_start": 1449685018,
"customer": "cus_7VCuNWxO5eWsQG",
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7VCuNWxO5eWsQG",
"subscription": "sub_7VCuZWrSt0bF8c"
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}

View file

@ -11,10 +11,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuNWxO5eWsQG",
"path": "/v1/customers/cus_7XTLw21mDCPduP",
"status": 200,
"response": {
"id": "cus_7VCuNWxO5eWsQG",
"id": "cus_7XTLw21mDCPduP",
"alipay_accounts": {
"has_more": false
},
@ -22,7 +22,7 @@
"coupon": {
"id": "20pct"
},
"customer": "cus_7VCuNWxO5eWsQG"
"customer": "cus_7XTLw21mDCPduP"
},
"email": "normal@jo.com",
"metadata": {
@ -31,8 +31,8 @@
"sources": {
"data": [
{
"id": "card_17GCUnKaReE7xLUdZ8FpRcR5",
"customer": "cus_7VCuNWxO5eWsQG"
"id": "card_17IOOlKaReE7xLUdwvPnfV15",
"customer": "cus_7XTLw21mDCPduP"
}
],
"has_more": false
@ -40,24 +40,24 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCuZWrSt0bF8c",
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452363418,
"current_period_start": 1449685018,
"customer": "cus_7VCuNWxO5eWsQG",
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7VCuNWxO5eWsQG",
"subscription": "sub_7VCuZWrSt0bF8c"
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -69,10 +69,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCuNWxO5eWsQG",
"path": "/v1/customers/cus_7XTLw21mDCPduP",
"status": 200,
"response": {
"id": "cus_7VCuNWxO5eWsQG",
"id": "cus_7XTLw21mDCPduP",
"alipay_accounts": {
"has_more": false
},
@ -80,7 +80,7 @@
"coupon": {
"id": "20pct"
},
"customer": "cus_7VCuNWxO5eWsQG"
"customer": "cus_7XTLw21mDCPduP"
},
"email": "normal@jo.com",
"metadata": {
@ -89,8 +89,8 @@
"sources": {
"data": [
{
"id": "card_17GCUnKaReE7xLUdZ8FpRcR5",
"customer": "cus_7VCuNWxO5eWsQG"
"id": "card_17IOOlKaReE7xLUdwvPnfV15",
"customer": "cus_7XTLw21mDCPduP"
}
],
"has_more": false
@ -98,24 +98,24 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCuZWrSt0bF8c",
"id": "sub_7XTLVSO3Guyty9",
"cancel_at_period_end": false,
"current_period_end": 1452363418,
"current_period_start": 1449685018,
"customer": "cus_7VCuNWxO5eWsQG",
"current_period_end": 1452885828,
"current_period_start": 1450207428,
"customer": "cus_7XTLw21mDCPduP",
"discount": {
"coupon": {
"id": "500off"
},
"customer": "cus_7VCuNWxO5eWsQG",
"subscription": "sub_7VCuZWrSt0bF8c"
"customer": "cus_7XTLw21mDCPduP",
"subscription": "sub_7XTLVSO3Guyty9"
},
"metadata": {
"id": "00000000000000000000005d"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVJKaReE7xLUdzpXOGZ9v",
"id": "tok_17IOPOKaReE7xLUdfaGNs2bY",
"card": {
"id": "card_17GCVJKaReE7xLUdKOWpllRX"
"id": "card_17IOPOKaReE7xLUdynADA8zk"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCvE20H99SoVJ",
"id": "cus_7XTMkthRYmfilo",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCVJKaReE7xLUdKOWpllRX",
"customer": "cus_7VCvE20H99SoVJ"
"id": "card_17IOPOKaReE7xLUdynADA8zk",
"customer": "cus_7XTMkthRYmfilo"
}
],
"has_more": false
@ -52,10 +52,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvE20H99SoVJ",
"path": "/v1/customers/cus_7XTMkthRYmfilo",
"status": 200,
"response": {
"id": "cus_7VCvE20H99SoVJ",
"id": "cus_7XTMkthRYmfilo",
"alipay_accounts": {
"has_more": false
},
@ -66,8 +66,8 @@
"sources": {
"data": [
{
"id": "card_17GCVJKaReE7xLUdKOWpllRX",
"customer": "cus_7VCvE20H99SoVJ"
"id": "card_17IOPOKaReE7xLUdynADA8zk",
"customer": "cus_7XTMkthRYmfilo"
}
],
"has_more": false
@ -83,21 +83,21 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCVLKaReE7xLUdb9DcZ4om",
"amount": 499,
"customer": "cus_7VCvE20H99SoVJ",
"id": "ch_17IOPQKaReE7xLUdsAmwl7fg",
"amount": 100,
"customer": "cus_7XTMkthRYmfilo",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685049575"
"timestamp": "1450207466249"
},
"paid": true,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCVJKaReE7xLUdKOWpllRX",
"customer": "cus_7VCvE20H99SoVJ"
"id": "card_17IOPOKaReE7xLUdynADA8zk",
"customer": "cus_7XTMkthRYmfilo"
}
}
},
@ -109,21 +109,21 @@
"response": {
"data": [
{
"id": "ch_17GCVLKaReE7xLUdb9DcZ4om",
"amount": 499,
"customer": "cus_7VCvE20H99SoVJ",
"id": "ch_17IOPQKaReE7xLUdsAmwl7fg",
"amount": 100,
"customer": "cus_7XTMkthRYmfilo",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685049575"
"timestamp": "1450207466249"
},
"paid": true,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCVJKaReE7xLUdKOWpllRX",
"customer": "cus_7VCvE20H99SoVJ"
"id": "card_17IOPOKaReE7xLUdynADA8zk",
"customer": "cus_7XTMkthRYmfilo"
}
}
],

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVNKaReE7xLUd2tmSQ1KH",
"id": "tok_17IOPSKaReE7xLUdyZv4jUA6",
"card": {
"id": "card_17GCVMKaReE7xLUdZVTFHMmq"
"id": "card_17IOPSKaReE7xLUdOOKz1MOG"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCvZ7KBrJ8csQ",
"id": "cus_7XTMBOnMy9jbYM",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCVMKaReE7xLUdZVTFHMmq",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPSKaReE7xLUdOOKz1MOG",
"customer": "cus_7XTMBOnMy9jbYM"
}
],
"has_more": false
@ -52,10 +52,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvZ7KBrJ8csQ",
"path": "/v1/customers/cus_7XTMBOnMy9jbYM",
"status": 200,
"response": {
"id": "cus_7VCvZ7KBrJ8csQ",
"id": "cus_7XTMBOnMy9jbYM",
"alipay_accounts": {
"has_more": false
},
@ -66,8 +66,8 @@
"sources": {
"data": [
{
"id": "card_17GCVMKaReE7xLUdZVTFHMmq",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPSKaReE7xLUdOOKz1MOG",
"customer": "cus_7XTMBOnMy9jbYM"
}
],
"has_more": false
@ -83,9 +83,9 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCVOKaReE7xLUd1ZQVMJKB",
"id": "ch_17IOPUKaReE7xLUd1gLXrdte",
"amount": 5000,
"customer": "cus_7VCvZ7KBrJ8csQ",
"customer": "cus_7XTMBOnMy9jbYM",
"metadata": {
"productID": "custom",
"timestamp": "1447445242091",
@ -96,8 +96,8 @@
"has_more": false
},
"source": {
"id": "card_17GCVMKaReE7xLUdZVTFHMmq",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPSKaReE7xLUdOOKz1MOG",
"customer": "cus_7XTMBOnMy9jbYM"
}
}
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVPKaReE7xLUdJcZimvbF",
"id": "tok_17IOPVKaReE7xLUdJAvZ79Fe",
"card": {
"id": "card_17GCVPKaReE7xLUdRwBCjoW4"
"id": "card_17IOPVKaReE7xLUdhslZrW4t"
},
"type": "card"
}
@ -15,10 +15,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvZ7KBrJ8csQ",
"path": "/v1/customers/cus_7XTMBOnMy9jbYM",
"status": 200,
"response": {
"id": "cus_7VCvZ7KBrJ8csQ",
"id": "cus_7XTMBOnMy9jbYM",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCVPKaReE7xLUdRwBCjoW4",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPVKaReE7xLUdhslZrW4t",
"customer": "cus_7XTMBOnMy9jbYM"
}
],
"has_more": false
@ -48,9 +48,9 @@
"response": {
"data": [
{
"id": "ch_17GCVOKaReE7xLUd1ZQVMJKB",
"id": "ch_17IOPUKaReE7xLUd1gLXrdte",
"amount": 5000,
"customer": "cus_7VCvZ7KBrJ8csQ",
"customer": "cus_7XTMBOnMy9jbYM",
"metadata": {
"productID": "custom",
"timestamp": "1447445242091",
@ -61,8 +61,8 @@
"has_more": false
},
"source": {
"id": "card_17GCVMKaReE7xLUdZVTFHMmq",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPSKaReE7xLUdOOKz1MOG",
"customer": "cus_7XTMBOnMy9jbYM"
}
}
],
@ -75,9 +75,9 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCVSKaReE7xLUdd5E7dowz",
"id": "ch_17IOPXKaReE7xLUdEinedgz6",
"amount": 73000,
"customer": "cus_7VCvZ7KBrJ8csQ",
"customer": "cus_7XTMBOnMy9jbYM",
"metadata": {
"productID": "custom",
"timestamp": "1447445242092"
@ -87,8 +87,8 @@
"has_more": false
},
"source": {
"id": "card_17GCVPKaReE7xLUdRwBCjoW4",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPVKaReE7xLUdhslZrW4t",
"customer": "cus_7XTMBOnMy9jbYM"
}
}
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVTKaReE7xLUdOnqynJY7",
"id": "tok_17IOPYKaReE7xLUdSnJV6x8N",
"card": {
"id": "card_17GCVSKaReE7xLUdjh0HgmFn"
"id": "card_17IOPYKaReE7xLUdXqbKgSlb"
},
"type": "card"
}
@ -15,10 +15,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvZ7KBrJ8csQ",
"path": "/v1/customers/cus_7XTMBOnMy9jbYM",
"status": 200,
"response": {
"id": "cus_7VCvZ7KBrJ8csQ",
"id": "cus_7XTMBOnMy9jbYM",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCVSKaReE7xLUdjh0HgmFn",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPYKaReE7xLUdXqbKgSlb",
"customer": "cus_7XTMBOnMy9jbYM"
}
],
"has_more": false
@ -48,9 +48,9 @@
"response": {
"data": [
{
"id": "ch_17GCVSKaReE7xLUdd5E7dowz",
"id": "ch_17IOPXKaReE7xLUdEinedgz6",
"amount": 73000,
"customer": "cus_7VCvZ7KBrJ8csQ",
"customer": "cus_7XTMBOnMy9jbYM",
"metadata": {
"productID": "custom",
"timestamp": "1447445242092"
@ -60,14 +60,14 @@
"has_more": false
},
"source": {
"id": "card_17GCVPKaReE7xLUdRwBCjoW4",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPVKaReE7xLUdhslZrW4t",
"customer": "cus_7XTMBOnMy9jbYM"
}
},
{
"id": "ch_17GCVOKaReE7xLUd1ZQVMJKB",
"id": "ch_17IOPUKaReE7xLUd1gLXrdte",
"amount": 5000,
"customer": "cus_7VCvZ7KBrJ8csQ",
"customer": "cus_7XTMBOnMy9jbYM",
"metadata": {
"productID": "custom",
"timestamp": "1447445242091",
@ -78,8 +78,8 @@
"has_more": false
},
"source": {
"id": "card_17GCVMKaReE7xLUdZVTFHMmq",
"customer": "cus_7VCvZ7KBrJ8csQ"
"id": "card_17IOPSKaReE7xLUdOOKz1MOG",
"customer": "cus_7XTMBOnMy9jbYM"
}
}
],

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCUvKaReE7xLUdo7q79Zbp",
"id": "tok_17IOOxKaReE7xLUdajQLXj2u",
"card": {
"id": "card_17GCUvKaReE7xLUdBp1HF9sT"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCubHuvGFt4nH",
"id": "cus_7XTLZvrxxi9h67",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
],
"has_more": false
@ -52,10 +52,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCubHuvGFt4nH",
"path": "/v1/customers/cus_7XTLZvrxxi9h67",
"status": 200,
"response": {
"id": "cus_7VCubHuvGFt4nH",
"id": "cus_7XTLZvrxxi9h67",
"alipay_accounts": {
"has_more": false
},
@ -66,8 +66,8 @@
"sources": {
"data": [
{
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
],
"has_more": false
@ -83,9 +83,9 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCUxKaReE7xLUddy1fN9Nl",
"amount": 499,
"customer": "cus_7VCubHuvGFt4nH",
"id": "ch_17IOOyKaReE7xLUdzY39hxln",
"amount": 100,
"customer": "cus_7XTLZvrxxi9h67",
"metadata": {
"productID": "gems_5",
"gems": "5000",
@ -96,8 +96,8 @@
"has_more": false
},
"source": {
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
}
}

View file

@ -2,7 +2,7 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCubHuvGFt4nH",
"path": "/v1/customers/cus_7XTLZvrxxi9h67",
"status": 400,
"response": {
"error": {
@ -18,9 +18,9 @@
"response": {
"data": [
{
"id": "ch_17GCUxKaReE7xLUddy1fN9Nl",
"amount": 499,
"customer": "cus_7VCubHuvGFt4nH",
"id": "ch_17IOOyKaReE7xLUdzY39hxln",
"amount": 100,
"customer": "cus_7XTLZvrxxi9h67",
"metadata": {
"productID": "gems_5",
"gems": "5000",
@ -31,8 +31,8 @@
"has_more": false
},
"source": {
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
}
],

View file

@ -7,9 +7,9 @@
"response": {
"data": [
{
"id": "ch_17GCUxKaReE7xLUddy1fN9Nl",
"amount": 499,
"customer": "cus_7VCubHuvGFt4nH",
"id": "ch_17IOOyKaReE7xLUdzY39hxln",
"amount": 100,
"customer": "cus_7XTLZvrxxi9h67",
"metadata": {
"productID": "gems_5",
"gems": "5000",
@ -20,8 +20,8 @@
"has_more": false
},
"source": {
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
}
],
@ -34,21 +34,21 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCUzKaReE7xLUdviRu5P6Z",
"amount": 499,
"customer": "cus_7VCubHuvGFt4nH",
"id": "ch_17IOP1KaReE7xLUdLHaawQjF",
"amount": 100,
"customer": "cus_7XTLZvrxxi9h67",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685028592"
"timestamp": "1450207442600"
},
"paid": true,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
}
}

View file

@ -2,10 +2,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCubHuvGFt4nH",
"path": "/v1/customers/cus_7XTLZvrxxi9h67",
"status": 200,
"response": {
"id": "cus_7VCubHuvGFt4nH",
"id": "cus_7XTLZvrxxi9h67",
"alipay_accounts": {
"has_more": false
},
@ -16,8 +16,8 @@
"sources": {
"data": [
{
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
],
"has_more": false
@ -33,9 +33,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCV0KaReE7xLUd6riHo5io",
"id": "tok_17IOP2KaReE7xLUdxvu0xWNX",
"card": {
"id": "card_17GCV0KaReE7xLUdA7hdLMnV"
"id": "card_17IOP2KaReE7xLUdgHcD4Uj5"
},
"type": "card"
}
@ -43,10 +43,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCubHuvGFt4nH",
"path": "/v1/customers/cus_7XTLZvrxxi9h67",
"status": 200,
"response": {
"id": "cus_7VCubHuvGFt4nH",
"id": "cus_7XTLZvrxxi9h67",
"alipay_accounts": {
"has_more": false
},
@ -57,8 +57,8 @@
"sources": {
"data": [
{
"id": "card_17GCV0KaReE7xLUdA7hdLMnV",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOP2KaReE7xLUdgHcD4Uj5",
"customer": "cus_7XTLZvrxxi9h67"
}
],
"has_more": false
@ -76,27 +76,27 @@
"response": {
"data": [
{
"id": "ch_17GCUzKaReE7xLUdviRu5P6Z",
"amount": 499,
"customer": "cus_7VCubHuvGFt4nH",
"id": "ch_17IOP1KaReE7xLUdLHaawQjF",
"amount": 100,
"customer": "cus_7XTLZvrxxi9h67",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685028592"
"timestamp": "1450207442600"
},
"paid": true,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
},
{
"id": "ch_17GCUxKaReE7xLUddy1fN9Nl",
"amount": 499,
"customer": "cus_7VCubHuvGFt4nH",
"id": "ch_17IOOyKaReE7xLUdzY39hxln",
"amount": 100,
"customer": "cus_7XTLZvrxxi9h67",
"metadata": {
"productID": "gems_5",
"gems": "5000",
@ -107,8 +107,8 @@
"has_more": false
},
"source": {
"id": "card_17GCUvKaReE7xLUdBp1HF9sT",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOOxKaReE7xLUdaA1sZ1RB",
"customer": "cus_7XTLZvrxxi9h67"
}
}
],
@ -121,31 +121,31 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCV2KaReE7xLUdffVpUhFO",
"amount": 499,
"customer": "cus_7VCubHuvGFt4nH",
"id": "ch_17IOP4KaReE7xLUdLWQ28NtO",
"amount": 100,
"customer": "cus_7XTLZvrxxi9h67",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685031026"
"timestamp": "1450207444904"
},
"paid": true,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV0KaReE7xLUdA7hdLMnV",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOP2KaReE7xLUdgHcD4Uj5",
"customer": "cus_7XTLZvrxxi9h67"
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCubHuvGFt4nH",
"path": "/v1/customers/cus_7XTLZvrxxi9h67",
"status": 200,
"response": {
"id": "cus_7VCubHuvGFt4nH",
"id": "cus_7XTLZvrxxi9h67",
"alipay_accounts": {
"has_more": false
},
@ -156,8 +156,8 @@
"sources": {
"data": [
{
"id": "card_17GCV0KaReE7xLUdA7hdLMnV",
"customer": "cus_7VCubHuvGFt4nH"
"id": "card_17IOP2KaReE7xLUdgHcD4Uj5",
"customer": "cus_7XTLZvrxxi9h67"
}
],
"has_more": false

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCV3KaReE7xLUdLcKedDEd",
"id": "tok_17IOP6KaReE7xLUdyel0tJtw",
"card": {
"id": "card_17GCV3KaReE7xLUdFUXfU85r"
"id": "card_17IOP6KaReE7xLUdvj1C0uY3"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCuAQ6nYInY8z",
"id": "cus_7XTLwWj4NCwj2S",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCV3KaReE7xLUdFUXfU85r",
"customer": "cus_7VCuAQ6nYInY8z"
"id": "card_17IOP6KaReE7xLUdvj1C0uY3",
"customer": "cus_7XTLwWj4NCwj2S"
}
],
"has_more": false
@ -52,10 +52,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuAQ6nYInY8z",
"path": "/v1/customers/cus_7XTLwWj4NCwj2S",
"status": 200,
"response": {
"id": "cus_7VCuAQ6nYInY8z",
"id": "cus_7XTLwWj4NCwj2S",
"alipay_accounts": {
"has_more": false
},
@ -66,8 +66,8 @@
"sources": {
"data": [
{
"id": "card_17GCV3KaReE7xLUdFUXfU85r",
"customer": "cus_7VCuAQ6nYInY8z"
"id": "card_17IOP6KaReE7xLUdvj1C0uY3",
"customer": "cus_7XTLwWj4NCwj2S"
}
],
"has_more": false
@ -83,9 +83,9 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCV5KaReE7xLUd0amzDJs7",
"amount": 499,
"customer": "cus_7VCuAQ6nYInY8z",
"id": "ch_17IOP7KaReE7xLUdDnFZwMu7",
"amount": 100,
"customer": "cus_7XTLwWj4NCwj2S",
"metadata": {
"productID": "gems_5",
"gems": "5000",
@ -96,15 +96,15 @@
"has_more": false
},
"source": {
"id": "card_17GCV3KaReE7xLUdFUXfU85r",
"customer": "cus_7VCuAQ6nYInY8z"
"id": "card_17IOP6KaReE7xLUdvj1C0uY3",
"customer": "cus_7XTLwWj4NCwj2S"
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuAQ6nYInY8z",
"path": "/v1/customers/cus_7XTLwWj4NCwj2S",
"status": 400,
"response": {
"error": {
@ -120,9 +120,9 @@
"response": {
"data": [
{
"id": "ch_17GCV5KaReE7xLUd0amzDJs7",
"amount": 499,
"customer": "cus_7VCuAQ6nYInY8z",
"id": "ch_17IOP7KaReE7xLUdDnFZwMu7",
"amount": 100,
"customer": "cus_7XTLwWj4NCwj2S",
"metadata": {
"productID": "gems_5",
"gems": "5000",
@ -133,8 +133,8 @@
"has_more": false
},
"source": {
"id": "card_17GCV3KaReE7xLUdFUXfU85r",
"customer": "cus_7VCuAQ6nYInY8z"
"id": "card_17IOP6KaReE7xLUdvj1C0uY3",
"customer": "cus_7XTLwWj4NCwj2S"
}
}
],
@ -147,9 +147,9 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCV6KaReE7xLUdiTvUGa07",
"amount": 499,
"customer": "cus_7VCuAQ6nYInY8z",
"id": "ch_17IOP9KaReE7xLUdEyubPCWU",
"amount": 100,
"customer": "cus_7XTLwWj4NCwj2S",
"metadata": {
"productID": "gems_5",
"gems": "5000",
@ -160,8 +160,8 @@
"has_more": false
},
"source": {
"id": "card_17GCV3KaReE7xLUdFUXfU85r",
"customer": "cus_7VCuAQ6nYInY8z"
"id": "card_17IOP6KaReE7xLUdvj1C0uY3",
"customer": "cus_7XTLwWj4NCwj2S"
}
}
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCV7KaReE7xLUd3gnnjv7x",
"id": "tok_17IOPAKaReE7xLUdrPE253ea",
"card": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd"
"id": "card_17IOPAKaReE7xLUdbLiixDCY"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCuzP303kt1eU",
"id": "cus_7XTL5uSTRsbBAg",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
],
"has_more": false
@ -52,10 +52,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuzP303kt1eU",
"path": "/v1/customers/cus_7XTL5uSTRsbBAg",
"status": 200,
"response": {
"id": "cus_7VCuzP303kt1eU",
"id": "cus_7XTL5uSTRsbBAg",
"alipay_accounts": {
"has_more": false
},
@ -66,8 +66,8 @@
"sources": {
"data": [
{
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
],
"has_more": false

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCV9KaReE7xLUdelU9sCaA",
"id": "tok_17IOPDKaReE7xLUdOTkDGNQH",
"card": {
"id": "card_17GCV9KaReE7xLUd5uODONlo"
"id": "card_17IOPDKaReE7xLUdOPYVzwbP"
},
"type": "card"
}
@ -15,7 +15,7 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuzP303kt1eU",
"path": "/v1/customers/cus_7XTL5uSTRsbBAg",
"status": 402,
"response": {
"error": {
@ -31,21 +31,21 @@
"response": {
"data": [
{
"id": "ch_17GCV9KaReE7xLUduvVirhN5",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPCKaReE7xLUdjfj5TqZH",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685037831"
"timestamp": "1450207452790"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
}
],

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVCKaReE7xLUdWbFQacYN",
"id": "tok_17IOPFKaReE7xLUdEKcEw4rn",
"card": {
"id": "card_17GCVCKaReE7xLUdjoXBJKqC"
"id": "card_17IOPFKaReE7xLUdIsElfCWI"
},
"type": "card"
}
@ -15,7 +15,7 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuzP303kt1eU",
"path": "/v1/customers/cus_7XTL5uSTRsbBAg",
"status": 402,
"response": {
"error": {
@ -31,39 +31,39 @@
"response": {
"data": [
{
"id": "ch_17GCVBKaReE7xLUduQybm6xT",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPFKaReE7xLUdToSxLUhB",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685040285"
"timestamp": "1450207455401"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
},
{
"id": "ch_17GCV9KaReE7xLUduvVirhN5",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPCKaReE7xLUdjfj5TqZH",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685037831"
"timestamp": "1450207452790"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
}
],

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVEKaReE7xLUdByVccSzA",
"id": "tok_17IOPIKaReE7xLUdohRtii5V",
"card": {
"id": "card_17GCVEKaReE7xLUdRUCeIUtP"
"id": "card_17IOPIKaReE7xLUdQ34s7FHR"
},
"type": "card"
}
@ -15,7 +15,7 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuzP303kt1eU",
"path": "/v1/customers/cus_7XTL5uSTRsbBAg",
"status": 402,
"response": {
"error": {
@ -31,57 +31,57 @@
"response": {
"data": [
{
"id": "ch_17GCVDKaReE7xLUdyZAPbXCM",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPIKaReE7xLUdReuRmdCl",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685042746"
"timestamp": "1450207457940"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
},
{
"id": "ch_17GCVBKaReE7xLUduQybm6xT",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPFKaReE7xLUdToSxLUhB",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685040285"
"timestamp": "1450207455401"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
},
{
"id": "ch_17GCV9KaReE7xLUduvVirhN5",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPCKaReE7xLUdjfj5TqZH",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685037831"
"timestamp": "1450207452790"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
}
],

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVHKaReE7xLUdB9odSMB6",
"id": "tok_17IOPLKaReE7xLUdiOrt8XEg",
"card": {
"id": "card_17GCVHKaReE7xLUdAYwa1Wkj"
"id": "card_17IOPLKaReE7xLUdUtDdouiy"
},
"type": "card"
}
@ -15,7 +15,7 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCuzP303kt1eU",
"path": "/v1/customers/cus_7XTL5uSTRsbBAg",
"status": 402,
"response": {
"error": {
@ -31,75 +31,75 @@
"response": {
"data": [
{
"id": "ch_17GCVGKaReE7xLUdu3HU9wB5",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPKKaReE7xLUdGocBlrbj",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685044928"
"timestamp": "1450207460901"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
},
{
"id": "ch_17GCVDKaReE7xLUdyZAPbXCM",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPIKaReE7xLUdReuRmdCl",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685042746"
"timestamp": "1450207457940"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
},
{
"id": "ch_17GCVBKaReE7xLUduQybm6xT",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPFKaReE7xLUdToSxLUhB",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685040285"
"timestamp": "1450207455401"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
},
{
"id": "ch_17GCV9KaReE7xLUduvVirhN5",
"amount": 499,
"customer": "cus_7VCuzP303kt1eU",
"id": "ch_17IOPCKaReE7xLUdjfj5TqZH",
"amount": 100,
"customer": "cus_7XTL5uSTRsbBAg",
"metadata": {
"productID": "gems_5",
"gems": "5000",
"timestamp": "1449685037831"
"timestamp": "1450207452790"
},
"paid": false,
"refunds": {
"has_more": false
},
"source": {
"id": "card_17GCV7KaReE7xLUdiPjgEUMd",
"customer": "cus_7VCuzP303kt1eU"
"id": "card_17IOPAKaReE7xLUdbLiixDCY",
"customer": "cus_7XTL5uSTRsbBAg"
}
}
],

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVVKaReE7xLUd9mYY9N7i",
"id": "tok_17IOPcKaReE7xLUd21HNj8Lx",
"card": {
"id": "card_17GCVVKaReE7xLUdlhNMXbGd"
"id": "card_17IOPcKaReE7xLUdk4xzxjp3"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVVKaReE7xLUdNnWusa8M",
"id": "tok_17IOPdKaReE7xLUdTLUKnG0J",
"card": {
"id": "card_17GCVVKaReE7xLUdNzakl6za"
"id": "card_17IOPdKaReE7xLUds6oneFJM"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCvt7iNlagXQA",
"id": "cus_7XTMzZwqOZpLXW",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCVVKaReE7xLUdNzakl6za",
"customer": "cus_7VCvt7iNlagXQA"
"id": "card_17IOPdKaReE7xLUds6oneFJM",
"customer": "cus_7XTMzZwqOZpLXW"
}
],
"has_more": false
@ -43,10 +43,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvt7iNlagXQA",
"path": "/v1/customers/cus_7XTMzZwqOZpLXW",
"status": 200,
"response": {
"id": "cus_7VCvt7iNlagXQA",
"id": "cus_7XTMzZwqOZpLXW",
"alipay_accounts": {
"has_more": false
},
@ -57,8 +57,8 @@
"sources": {
"data": [
{
"id": "card_17GCVVKaReE7xLUdNzakl6za",
"customer": "cus_7VCvt7iNlagXQA"
"id": "card_17IOPdKaReE7xLUds6oneFJM",
"customer": "cus_7XTMzZwqOZpLXW"
}
],
"has_more": false
@ -74,12 +74,12 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCVWKaReE7xLUdZLmqWCt6",
"amount": 400,
"customer": "cus_7VCvt7iNlagXQA",
"id": "ch_17IOPeKaReE7xLUd3o8mN5yw",
"amount": 100,
"customer": "cus_7XTMzZwqOZpLXW",
"metadata": {
"type": "course",
"timestamp": "1449685061856",
"timestamp": "1450207481311",
"productID": "prepaid course"
},
"paid": true,
@ -87,8 +87,8 @@
"has_more": false
},
"source": {
"id": "card_17GCVVKaReE7xLUdNzakl6za",
"customer": "cus_7VCvt7iNlagXQA"
"id": "card_17IOPdKaReE7xLUds6oneFJM",
"customer": "cus_7XTMzZwqOZpLXW"
}
}
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVXKaReE7xLUdOVrQZisx",
"id": "tok_17IOQgKaReE7xLUd7Cj6SMxq",
"card": {
"id": "card_17GCVXKaReE7xLUdFEluXOeu"
"id": "card_17IOQgKaReE7xLUd2XI9u2el"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCvUi50nXhjWn",
"id": "cus_7XTN61mUy646vB",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,36 @@
"sources": {
"data": [
{
"id": "card_17GCVXKaReE7xLUdFEluXOeu",
"customer": "cus_7VCvUi50nXhjWn"
"id": "card_17IOQgKaReE7xLUd2XI9u2el",
"customer": "cus_7XTN61mUy646vB"
}
],
"has_more": false
},
"subscriptions": {
"has_more": false
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7XTN61mUy646vB",
"status": 200,
"response": {
"id": "cus_7XTN61mUy646vB",
"alipay_accounts": {
"has_more": false
},
"email": "user89@me.com",
"metadata": {
"id": "000000000000000000000081"
},
"sources": {
"data": [
{
"id": "card_17IOQgKaReE7xLUd2XI9u2el",
"customer": "cus_7XTN61mUy646vB"
}
],
"has_more": false
@ -46,12 +74,12 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCVYKaReE7xLUdstf63Kol",
"amount": 1200,
"customer": "cus_7VCvUi50nXhjWn",
"id": "ch_17IOQhKaReE7xLUd4dyiqDny",
"amount": 300,
"customer": "cus_7XTN61mUy646vB",
"metadata": {
"type": "course",
"timestamp": "1449685063457",
"timestamp": "1450207546495",
"productID": "prepaid course"
},
"paid": true,
@ -59,8 +87,8 @@
"has_more": false
},
"source": {
"id": "card_17GCVXKaReE7xLUdFEluXOeu",
"customer": "cus_7VCvUi50nXhjWn"
"id": "card_17IOQgKaReE7xLUd2XI9u2el",
"customer": "cus_7XTN61mUy646vB"
}
}
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVZKaReE7xLUdc2sd97Kh",
"id": "tok_17IOQiKaReE7xLUdJL3Q7JLp",
"card": {
"id": "card_17GCVZKaReE7xLUdAQ3WCuCA"
"id": "card_17IOQiKaReE7xLUd40BrSMNC"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVZKaReE7xLUdLm4xB9Q4",
"id": "tok_17IOQjKaReE7xLUdBLKEWC4R",
"card": {
"id": "card_17GCVZKaReE7xLUdfv42GBHU"
"id": "card_17IOQjKaReE7xLUdCbcBVuuk"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVaKaReE7xLUdQP3U2KmC",
"id": "tok_17IOQjKaReE7xLUdQx1dIheo",
"card": {
"id": "card_17GCVaKaReE7xLUdCItK5QND"
"id": "card_17IOQjKaReE7xLUdPcrGFELH"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVaKaReE7xLUdfcArqe1K",
"id": "tok_17IOQkKaReE7xLUdN8oUK1vt",
"card": {
"id": "card_17GCVaKaReE7xLUdQXuE34GK"
"id": "card_17IOQkKaReE7xLUd89WMMmJy"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVaKaReE7xLUdZqrHnDcB",
"id": "tok_17IOQlKaReE7xLUdvcM60UvZ",
"card": {
"id": "card_17GCVaKaReE7xLUdu0XGOs3Q"
"id": "card_17IOQlKaReE7xLUd6KipB1W5"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVbKaReE7xLUdZSSwB8CG",
"id": "tok_17IOQmKaReE7xLUdOrulQKjc",
"card": {
"id": "card_17GCVbKaReE7xLUdsdKXR63y"
"id": "card_17IOQmKaReE7xLUd412VlEor"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVbKaReE7xLUdswrosKLO",
"id": "tok_17IOQmKaReE7xLUdpGCNDFq8",
"card": {
"id": "card_17GCVbKaReE7xLUdiINiC8ab"
"id": "card_17IOQmKaReE7xLUd0t08mRbC"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVcKaReE7xLUdB5HnT2wI",
"id": "tok_17IOQnKaReE7xLUd7vxmypxi",
"card": {
"id": "card_17GCVcKaReE7xLUd7R94nEhN"
"id": "card_17IOQnKaReE7xLUdwvfk7NXJ"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCvcfu4XzeCbM",
"id": "cus_7XTNzqLiTWHO6t",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCVcKaReE7xLUd7R94nEhN",
"customer": "cus_7VCvcfu4XzeCbM"
"id": "card_17IOQnKaReE7xLUdwvfk7NXJ",
"customer": "cus_7XTNzqLiTWHO6t"
}
],
"has_more": false
@ -43,61 +43,78 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t/subscriptions",
"status": 200,
"response": {
"id": "cus_7VCvcfu4XzeCbM",
"alipay_accounts": {
"has_more": false
},
"email": "normal@jo.com",
"metadata": {
"id": "000000000000000000000088"
},
"sources": {
"data": [
{
"id": "card_17GCVcKaReE7xLUd7R94nEhN",
"customer": "cus_7VCvcfu4XzeCbM"
}
],
"has_more": false
},
"subscriptions": {
"has_more": false
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM/subscriptions",
"status": 200,
"response": {
"id": "sub_7VCvEG141oJimL",
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452363470,
"current_period_start": 1449685070,
"customer": "cus_7VCvcfu4XzeCbM",
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
},
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t",
"status": 200,
"response": {
"id": "cus_7XTNzqLiTWHO6t",
"alipay_accounts": {
"has_more": false
},
"email": "normal@jo.com",
"metadata": {
"id": "000000000000000000000088"
},
"sources": {
"data": [
{
"id": "card_17IOQnKaReE7xLUdwvfk7NXJ",
"customer": "cus_7XTNzqLiTWHO6t"
}
],
"has_more": false
},
"subscriptions": {
"data": [
{
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 100
},
"quantity": 1
}
],
"has_more": false
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVfKaReE7xLUdIvigMi5U",
"id": "tok_17IOQqKaReE7xLUdvDFV7zxM",
"card": {
"id": "card_17GCVfKaReE7xLUdks8zeqw5"
"id": "card_17IOQqKaReE7xLUdRqwKR3Vo"
},
"type": "card"
}
@ -105,10 +122,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t",
"status": 200,
"response": {
"id": "cus_7VCvcfu4XzeCbM",
"id": "cus_7XTNzqLiTWHO6t",
"alipay_accounts": {
"has_more": false
},
@ -119,8 +136,8 @@
"sources": {
"data": [
{
"id": "card_17GCVcKaReE7xLUd7R94nEhN",
"customer": "cus_7VCvcfu4XzeCbM"
"id": "card_17IOQnKaReE7xLUdwvfk7NXJ",
"customer": "cus_7XTNzqLiTWHO6t"
}
],
"has_more": false
@ -128,17 +145,17 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCvEG141oJimL",
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452363470,
"current_period_start": 1449685070,
"customer": "cus_7VCvcfu4XzeCbM",
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -150,10 +167,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t",
"status": 200,
"response": {
"id": "cus_7VCvcfu4XzeCbM",
"id": "cus_7XTNzqLiTWHO6t",
"alipay_accounts": {
"has_more": false
},
@ -164,8 +181,8 @@
"sources": {
"data": [
{
"id": "card_17GCVfKaReE7xLUdks8zeqw5",
"customer": "cus_7VCvcfu4XzeCbM"
"id": "card_17IOQnKaReE7xLUdwvfk7NXJ",
"customer": "cus_7XTNzqLiTWHO6t"
}
],
"has_more": false
@ -173,17 +190,17 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCvEG141oJimL",
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452363470,
"current_period_start": 1449685070,
"customer": "cus_7VCvcfu4XzeCbM",
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -195,10 +212,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t",
"status": 200,
"response": {
"id": "cus_7VCvcfu4XzeCbM",
"id": "cus_7XTNzqLiTWHO6t",
"alipay_accounts": {
"has_more": false
},
@ -209,8 +226,8 @@
"sources": {
"data": [
{
"id": "card_17GCVfKaReE7xLUdks8zeqw5",
"customer": "cus_7VCvcfu4XzeCbM"
"id": "card_17IOQqKaReE7xLUdRqwKR3Vo",
"customer": "cus_7XTNzqLiTWHO6t"
}
],
"has_more": false
@ -218,17 +235,17 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCvEG141oJimL",
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452363470,
"current_period_start": 1449685070,
"customer": "cus_7VCvcfu4XzeCbM",
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -243,12 +260,12 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCVgKaReE7xLUdTqpuyEWR",
"amount": 8991,
"customer": "cus_7VCvcfu4XzeCbM",
"id": "ch_17IOQsKaReE7xLUd6vAsPLa6",
"amount": 900,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"type": "terminal_subscription",
"timestamp": "1449685071367",
"timestamp": "1450207556958",
"months": "3",
"productID": "prepaid terminal_subscription"
},
@ -257,8 +274,8 @@
"has_more": false
},
"source": {
"id": "card_17GCVfKaReE7xLUdks8zeqw5",
"customer": "cus_7VCvcfu4XzeCbM"
"id": "card_17IOQqKaReE7xLUdRqwKR3Vo",
"customer": "cus_7XTNzqLiTWHO6t"
}
}
}

View file

@ -2,10 +2,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t",
"status": 200,
"response": {
"id": "cus_7VCvcfu4XzeCbM",
"id": "cus_7XTNzqLiTWHO6t",
"alipay_accounts": {
"has_more": false
},
@ -16,8 +16,8 @@
"sources": {
"data": [
{
"id": "card_17GCVfKaReE7xLUdks8zeqw5",
"customer": "cus_7VCvcfu4XzeCbM"
"id": "card_17IOQqKaReE7xLUdRqwKR3Vo",
"customer": "cus_7XTNzqLiTWHO6t"
}
],
"has_more": false
@ -25,17 +25,17 @@
"subscriptions": {
"data": [
{
"id": "sub_7VCvEG141oJimL",
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452363470,
"current_period_start": 1449685070,
"customer": "cus_7VCvcfu4XzeCbM",
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -47,22 +47,22 @@
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM/subscriptions",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t/subscriptions",
"status": 200,
"response": {
"data": [
{
"id": "sub_7VCvEG141oJimL",
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452363470,
"current_period_start": 1449685070,
"customer": "cus_7VCvcfu4XzeCbM",
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -73,22 +73,22 @@
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM/subscriptions",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t/subscriptions",
"status": 200,
"response": {
"data": [
{
"id": "sub_7VCvEG141oJimL",
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452363470,
"current_period_start": 1449685070,
"customer": "cus_7VCvcfu4XzeCbM",
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -99,20 +99,20 @@
{
"scope": "https://api.stripe.com:443",
"method": "DELETE",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM/subscriptions/sub_7VCvEG141oJimL",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t/subscriptions/sub_7XTN0j8yRl2LLW",
"status": 200,
"response": {
"id": "sub_7VCvEG141oJimL",
"id": "sub_7XTN0j8yRl2LLW",
"cancel_at_period_end": false,
"current_period_end": 1452363470,
"current_period_start": 1449685070,
"customer": "cus_7VCvcfu4XzeCbM",
"current_period_end": 1452885954,
"current_period_start": 1450207554,
"customer": "cus_7XTNzqLiTWHO6t",
"metadata": {
"id": "000000000000000000000088"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}
@ -120,10 +120,10 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t",
"status": 200,
"response": {
"id": "cus_7VCvcfu4XzeCbM",
"id": "cus_7XTNzqLiTWHO6t",
"alipay_accounts": {
"has_more": false
},
@ -134,8 +134,36 @@
"sources": {
"data": [
{
"id": "card_17GCVfKaReE7xLUdks8zeqw5",
"customer": "cus_7VCvcfu4XzeCbM"
"id": "card_17IOQqKaReE7xLUdRqwKR3Vo",
"customer": "cus_7XTNzqLiTWHO6t"
}
],
"has_more": false
},
"subscriptions": {
"has_more": false
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t",
"status": 200,
"response": {
"id": "cus_7XTNzqLiTWHO6t",
"alipay_accounts": {
"has_more": false
},
"email": "normal@jo.com",
"metadata": {
"id": "000000000000000000000088"
},
"sources": {
"data": [
{
"id": "card_17IOQqKaReE7xLUdRqwKR3Vo",
"customer": "cus_7XTNzqLiTWHO6t"
}
],
"has_more": false
@ -148,7 +176,7 @@
{
"scope": "https://api.stripe.com:443",
"method": "GET",
"path": "/v1/customers/cus_7VCvcfu4XzeCbM/subscriptions",
"path": "/v1/customers/cus_7XTNzqLiTWHO6t/subscriptions",
"status": 200,
"response": {
"has_more": false

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVlKaReE7xLUdrgB5G5sg",
"id": "tok_17IOQxKaReE7xLUdIT2cvKAt",
"card": {
"id": "card_17GCVkKaReE7xLUdhnarn1rx"
"id": "card_17IOQxKaReE7xLUdls71CTWN"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCvhVTldJ0ArC",
"id": "cus_7XTN2QmGLf0x5K",
"alipay_accounts": {
"has_more": false
},
@ -29,36 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCVkKaReE7xLUdhnarn1rx",
"customer": "cus_7VCvhVTldJ0ArC"
}
],
"has_more": false
},
"subscriptions": {
"has_more": false
}
}
},
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvhVTldJ0ArC",
"status": 200,
"response": {
"id": "cus_7VCvhVTldJ0ArC",
"alipay_accounts": {
"has_more": false
},
"email": "user98@me.com",
"metadata": {
"id": "00000000000000000000008c"
},
"sources": {
"data": [
{
"id": "card_17GCVkKaReE7xLUdhnarn1rx",
"customer": "cus_7VCvhVTldJ0ArC"
"id": "card_17IOQxKaReE7xLUdls71CTWN",
"customer": "cus_7XTN2QmGLf0x5K"
}
],
"has_more": false
@ -74,12 +46,12 @@
"path": "/v1/charges",
"status": 200,
"response": {
"id": "ch_17GCVlKaReE7xLUd5D6k1v4E",
"amount": 2997,
"customer": "cus_7VCvhVTldJ0ArC",
"id": "ch_17IOQyKaReE7xLUdftH7lIJF",
"amount": 300,
"customer": "cus_7XTN2QmGLf0x5K",
"metadata": {
"type": "terminal_subscription",
"timestamp": "1449685077119",
"timestamp": "1450207563299",
"months": "1",
"productID": "prepaid terminal_subscription"
},
@ -88,8 +60,8 @@
"has_more": false
},
"source": {
"id": "card_17GCVkKaReE7xLUdhnarn1rx",
"customer": "cus_7VCvhVTldJ0ArC"
"id": "card_17IOQxKaReE7xLUdls71CTWN",
"customer": "cus_7XTN2QmGLf0x5K"
}
}
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVmKaReE7xLUd3TTueitH",
"id": "tok_17IOHcKaReE7xLUdfKtGmLgV",
"card": {
"id": "card_17GCVmKaReE7xLUdErwdQ45K"
"id": "card_17IOHcKaReE7xLUdAusILb3u"
},
"type": "card"
}

View file

@ -5,9 +5,9 @@
"path": "/v1/tokens",
"status": 200,
"response": {
"id": "tok_17GCVoKaReE7xLUdWKRMzFDb",
"id": "tok_17IOHeKaReE7xLUd0xVR8Qcu",
"card": {
"id": "card_17GCVoKaReE7xLUdpf0crA0p"
"id": "card_17IOHeKaReE7xLUdsx1siboK"
},
"type": "card"
}
@ -18,7 +18,7 @@
"path": "/v1/customers",
"status": 200,
"response": {
"id": "cus_7VCvUEDSyqS8aN",
"id": "cus_7XTE2tCcVgwJps",
"alipay_accounts": {
"has_more": false
},
@ -29,8 +29,8 @@
"sources": {
"data": [
{
"id": "card_17GCVoKaReE7xLUdpf0crA0p",
"customer": "cus_7VCvUEDSyqS8aN"
"id": "card_17IOHeKaReE7xLUdsx1siboK",
"customer": "cus_7XTE2tCcVgwJps"
}
],
"has_more": false
@ -43,20 +43,20 @@
{
"scope": "https://api.stripe.com:443",
"method": "POST",
"path": "/v1/customers/cus_7VCvUEDSyqS8aN/subscriptions",
"path": "/v1/customers/cus_7XTE2tCcVgwJps/subscriptions",
"status": 200,
"response": {
"id": "sub_7VCvXO37PhGOUY",
"id": "sub_7XTEeDaljXocIE",
"cancel_at_period_end": false,
"current_period_end": 1452363481,
"current_period_start": 1449685081,
"customer": "cus_7VCvUEDSyqS8aN",
"current_period_end": 1452885387,
"current_period_start": 1450206987,
"customer": "cus_7XTE2tCcVgwJps",
"metadata": {
"id": "0000000000000000000186a1"
},
"plan": {
"id": "basic",
"amount": 999
"amount": 100
},
"quantity": 1
}

Some files were not shown because too many files have changed in this diff Show more