├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── backend ├── README.md ├── alembic.ini ├── app │ ├── __init__.py │ ├── bl │ │ ├── __init__.py │ │ └── product.py │ ├── constants.py │ ├── core.py │ ├── extensions │ │ └── __init__.py │ ├── factory.py │ ├── helpers │ │ ├── __init__.py │ │ ├── decorators.py │ │ ├── message.py │ │ ├── response.py │ │ └── utils.py │ ├── models │ │ └── __init__.py │ ├── routes │ │ ├── __init__.py │ │ └── product.py │ ├── settings-default.py │ ├── settings.cfg │ └── tasks │ │ └── __init__.py ├── logging.conf ├── logs │ └── .gitkeep ├── manage.py ├── migrations │ ├── README │ ├── env.py │ └── script.py.mako ├── requirements.txt ├── start.sh ├── tests │ └── __init__.py ├── uwsgi.ini └── wsgi.py ├── flutter_midtrans ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── .project │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── flutter_midtrans │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── orange_helmet.png │ ├── top_bg.svg │ ├── unbranded_helmet.png │ └── xiaomi_helmet.png ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── blocs │ │ └── product │ │ │ ├── product_cubit.dart │ │ │ └── product_state.dart │ ├── config │ │ ├── config.dart │ │ ├── main_dev.dart │ │ ├── main_local.dart │ │ └── main_prod.dart │ ├── data │ │ ├── local │ │ │ └── storage.dart │ │ └── models │ │ │ └── product.dart │ ├── helpers │ │ ├── api_response.dart │ │ ├── error_codes.dart │ │ ├── logger │ │ │ ├── log_filter.dart │ │ │ ├── log_printer.dart │ │ │ └── tokoin_log.dart │ │ ├── theme │ │ │ ├── theme_dark.dart │ │ │ ├── theme_light.dart │ │ │ ├── theme_provider.dart │ │ │ └── themes.dart │ │ └── utils.dart │ ├── main.dart │ ├── main_configured.dart │ ├── repositories │ │ └── product_repo.dart │ ├── root.dart │ ├── routes.dart │ ├── screen_router.dart │ ├── screens │ │ ├── home.dart │ │ └── snap.dart │ ├── services │ │ ├── api.dart │ │ └── response.dart │ └── widgets │ │ └── common │ │ └── button_widget.dart ├── pubspec.yaml └── test │ └── widget_test.dart └── screenshots ├── screen_shot_1.png ├── screen_shot_2.png └── screen_shot_3.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | build/ 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | pubspec.lock 9 | 10 | # Directory created by dartdoc 11 | # If you don't generate documentation locally you can remove this line. 12 | doc/api/ 13 | 14 | # Avoid committing generated Javascript files: 15 | *.dart.js 16 | *.info.json # Produced by the --dump-info flag. 17 | *.js # When generated by dart2js. Don't specify *.js if your 18 | # project includes source files written in JavaScript. 19 | *.js_ 20 | *.js.deps 21 | *.js.map 22 | 23 | # Byte-compiled / optimized / DLL files 24 | __pycache__/ 25 | *.py[cod] 26 | *$py.class 27 | 28 | # C extensions 29 | *.so 30 | 31 | # Distribution / packaging 32 | .Python 33 | build/ 34 | develop-eggs/ 35 | dist/ 36 | downloads/ 37 | eggs/ 38 | .eggs/ 39 | # lib/ 40 | lib64/ 41 | parts/ 42 | sdist/ 43 | var/ 44 | wheels/ 45 | *.egg-info/ 46 | .installed.cfg 47 | *.egg 48 | MANIFEST 49 | 50 | # PyInstaller 51 | # Usually these files are written by a python script from a template 52 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 53 | *.manifest 54 | *.spec 55 | 56 | # Installer logs 57 | pip-log.txt 58 | pip-delete-this-directory.txt 59 | 60 | # Unit test / coverage reports 61 | htmlcov/ 62 | .tox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | nosetests.xml 67 | coverage.xml 68 | *.cover 69 | .hypothesis/ 70 | .pytest_cache/ 71 | 72 | # Translations 73 | *.mo 74 | *.pot 75 | 76 | # Django stuff: 77 | *.log 78 | local_settings.py 79 | db.sqlite3 80 | 81 | # Flask stuff: 82 | instance/ 83 | .webassets-cache 84 | 85 | # Scrapy stuff: 86 | .scrapy 87 | 88 | # Sphinx documentation 89 | docs/_build/ 90 | 91 | # PyBuilder 92 | target/ 93 | 94 | # Jupyter Notebook 95 | .ipynb_checkpoints 96 | 97 | # pyenv 98 | .python-version 99 | 100 | # celery beat schedule file 101 | celerybeat-schedule 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | 128 | 129 | # restapi ignore 130 | backend/.env 131 | backend/app/settings.py 132 | dump.rdb 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | An example how to use [midtrans](https://midtrans.com/) as a payment gateway on both of mobile and backend. 2 | 3 | ### Show some :heart: and star the repo to support the project 4 | 5 | ### How to Run: 6 | 7 | #### Backend: 8 | 9 | - cd backend 10 | - virtualenv .venv 11 | - source .venv/bin/activate 12 | - pip install -r requirements.txt 13 | - cp app/settings-default.py app/settings.py 14 | - replace `MIDTRANS_SERVER_KEY` and `MIDTRANS_CLIENT_KEY` with your current key in app/settings.py 15 | - python3 wsgi.py 16 | 17 | #### Mobile: 18 | 19 | - cd flutter_midtrans 20 | - replace `MIDTRANS_CLIENT_KEY` in lib/config/config.dart 21 | - flutter run 22 | 23 | ### Screenshots 24 | 25 | 26 | 27 | ### Created & Maintained By 28 | 29 | [Trong Dinh](https://github.com/trongdth) ([@trongdth](https://www.twitter.com/trongdth)) 30 | 31 | > If you found this project helpful or you learned something from the source code and want to thank me, consider buying me a cup of :coffee: 32 | > 33 | > * [Ethereum address: 0x9a1592C20A15f99AbB6b69E199f38D50Fa8372Ac] 34 | > * [EOS account: zcryptoman1z] 35 | 36 | ## Getting Started 37 | 38 | - For help getting started with Flutter, view [online documentation](https://flutter.dev/docs), which offers tutorials, 39 | samples, guidance on mobile development, and a full API reference. 40 | 41 | - For help getting started with Python Flask, view [online documentation](https://github.com/pallets/flask), which offers tutorials, 42 | samples, and a full API reference. 43 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | #### Backend: 2 | 3 | - virtualenv .venv 4 | - source .venv/bin/activate 5 | - pip install -r requirements.txt 6 | - cp app/settings-default.py app/settings.py 7 | - replace `MIDTRANS_SERVER_KEY` and `MIDTRANS_CLIENT_KEY` with your current key in app/settings.py 8 | - python3 wsgi.py -------------------------------------------------------------------------------- /backend/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = migrations 6 | 7 | # template used to generate migration files 8 | # file_template = %%(rev)s_%%(slug)s 9 | 10 | # timezone to use when rendering the date 11 | # within the migration file as well as the filename. 12 | # string value is passed to dateutil.tz.gettz() 13 | # leave blank for localtime 14 | # timezone = 15 | 16 | # max length of characters to apply to the 17 | # "slug" field 18 | # truncate_slug_length = 40 19 | 20 | # set to 'true' to run the environment during 21 | # the 'revision' command, regardless of autogenerate 22 | # revision_environment = false 23 | 24 | # set to 'true' to allow .pyc and .pyo files without 25 | # a source .py file to be detected as revisions in the 26 | # versions/ directory 27 | # sourceless = false 28 | 29 | # version location specification; this defaults 30 | # to migrations/versions. When using multiple version 31 | # directories, initial revisions must be specified with --version-path 32 | # version_locations = %(here)s/bar %(here)s/bat migrations/versions 33 | 34 | # the output encoding used when revision files 35 | # are written from script.py.mako 36 | # output_encoding = utf-8 37 | 38 | # sqlalchemy.url = driver://user:pass@localhost/dbname 39 | 40 | 41 | [post_write_hooks] 42 | # post_write_hooks defines scripts or Python functions that are run 43 | # on newly generated revision scripts. See the documentation for further 44 | # detail and examples 45 | 46 | # format using "black" - use the console_scripts runner, against the "black" entrypoint 47 | # hooks=black 48 | # black.type=console_scripts 49 | # black.entrypoint=black 50 | # black.options=-l 79 51 | 52 | # Logging configuration 53 | [loggers] 54 | keys = root,sqlalchemy,alembic 55 | 56 | [handlers] 57 | keys = console 58 | 59 | [formatters] 60 | keys = generic 61 | 62 | [logger_root] 63 | level = WARN 64 | handlers = console 65 | qualname = 66 | 67 | [logger_sqlalchemy] 68 | level = WARN 69 | handlers = 70 | qualname = sqlalchemy.engine 71 | 72 | [logger_alembic] 73 | level = INFO 74 | handlers = 75 | qualname = alembic 76 | 77 | [handler_console] 78 | class = StreamHandler 79 | args = (sys.stderr,) 80 | level = NOTSET 81 | formatter = generic 82 | 83 | [formatter_generic] 84 | format = %(levelname)-5.5s [%(name)s] %(message)s 85 | datefmt = %H:%M:%S 86 | -------------------------------------------------------------------------------- /backend/app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, g, redirect, request 2 | from app.core import db, jwt, configure_app 3 | from flask_cors import CORS 4 | from app.helpers.response import response_error 5 | from app.routes import init_routes 6 | from datetime import datetime 7 | 8 | import time 9 | import decimal 10 | import flask.json 11 | import logging 12 | import logging.config 13 | import yaml 14 | 15 | 16 | class MyJSONEncoder(flask.json.JSONEncoder): 17 | def default(self, obj): 18 | if isinstance(obj, decimal.Decimal): 19 | # Convert decimal instances to strings. 20 | return str(float(obj)) 21 | return super(MyJSONEncoder, self).default(obj) 22 | 23 | 24 | app = Flask(__name__) 25 | 26 | logging.config.dictConfig( 27 | yaml.load(open('logging.conf'), Loader=yaml.FullLoader)) 28 | logfile = logging.getLogger('file') 29 | 30 | # add json encoder for decimal type 31 | app.json_encoder = MyJSONEncoder 32 | # disable strict_slashes 33 | app.url_map.strict_slashes = False 34 | # config app 35 | configure_app(app) 36 | 37 | # Accept CORS 38 | CORS(app) 39 | # init db 40 | db.init_app(app) 41 | # init jwt 42 | jwt.init_app(app) 43 | 44 | 45 | @app.before_request 46 | def before_request(): 47 | rp = request.path 48 | if rp != '/' and rp.endswith('/'): 49 | return redirect(rp[:-1]) 50 | 51 | g.MIDTRANS_SERVER_KEY = app.config.get('MIDTRANS_SERVER_KEY') 52 | g.MIDTRANS_CLIENT_KEY = app.config.get('MIDTRANS_CLIENT_KEY') 53 | g.ENV = app.config.get('ENV') 54 | 55 | 56 | @app.after_request 57 | def after_request(response): 58 | return response 59 | 60 | 61 | init_routes(app) 62 | 63 | 64 | def jwt_error_handler(message): 65 | return response_error(message) 66 | 67 | 68 | def needs_fresh_token_callback(): 69 | return response_error('Only fresh tokens are allowed') 70 | 71 | 72 | def revoked_token_callback(): 73 | return response_error('Token has been revoked') 74 | 75 | 76 | def expired_token_callback(): 77 | return response_error('Token has expired', 401) 78 | 79 | 80 | jwt.unauthorized_loader(jwt_error_handler) 81 | jwt.invalid_token_loader(jwt_error_handler) 82 | jwt.claims_verification_loader(jwt_error_handler) 83 | jwt.token_in_blacklist_loader(jwt_error_handler) 84 | jwt.user_loader_error_loader(jwt_error_handler) 85 | jwt.claims_verification_failed_loader(jwt_error_handler) 86 | jwt.expired_token_loader(expired_token_callback) 87 | jwt.needs_fresh_token_loader(needs_fresh_token_callback) 88 | jwt.revoked_token_loader(revoked_token_callback) 89 | 90 | 91 | @app.errorhandler(Exception) 92 | def error_handler(err): 93 | return response_error(str(err)) 94 | -------------------------------------------------------------------------------- /backend/app/bl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/backend/app/bl/__init__.py -------------------------------------------------------------------------------- /backend/app/bl/product.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from flask import g 4 | 5 | import base64 6 | 7 | 8 | def generate_auth_header_value(): 9 | key = base64.b64encode( 10 | bytes(g.MIDTRANS_SERVER_KEY, 'utf-8')) 11 | return "Basic {}".format(key.decode("ascii")) 12 | 13 | 14 | def prepare_headers(json=True): 15 | header = {} 16 | if json: 17 | header["Content-Type"] = "application/json" 18 | header["Accept"] = "application/json" 19 | header["Authorization"] = generate_auth_header_value() 20 | return header 21 | -------------------------------------------------------------------------------- /backend/app/constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /backend/app/core.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask_jwt_extended import JWTManager 4 | from flask_sqlalchemy import SQLAlchemy 5 | 6 | db = SQLAlchemy() 7 | jwt = JWTManager() 8 | 9 | # configure app from env + silent local settings.cfg 10 | 11 | 12 | def configure_app(app): 13 | 14 | base_dir = os.path.abspath(os.path.dirname(__file__)) 15 | if not os.path.exists(base_dir + "/settings.py"): 16 | os.rename(base_dir + "/settings-default.py", base_dir + "/settings.py") 17 | 18 | if not os.path.exists(base_dir + "/settings.cfg"): 19 | os.rename(base_dir + "/settings-default.cfg", 20 | base_dir + "/settings.cfg") 21 | 22 | config = { 23 | "development": "app.settings.DevelopmentConfig", 24 | "default": "app.settings.DevelopmentConfig" 25 | } 26 | config_name = os.getenv('PYTHON_ENV', 'default') 27 | # object-based default configuration 28 | app.config.from_object(config[config_name]) 29 | app.config.from_pyfile(os.path.dirname(os.path.realpath( 30 | __file__)) + '/settings.cfg', silent=True) # instance-folders configuration 31 | -------------------------------------------------------------------------------- /backend/app/extensions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/backend/app/extensions/__init__.py -------------------------------------------------------------------------------- /backend/app/factory.py: -------------------------------------------------------------------------------- 1 | from celery import Celery 2 | 3 | 4 | def make_celery(app): 5 | celery = Celery(__name__, broker=app.config['CELERY_BROKER_URL']) 6 | celery.conf.update(app.config) 7 | TaskBase = celery.Task 8 | 9 | class ContextTask(TaskBase): 10 | abstract = True 11 | 12 | def __call__(self, *args, **kwargs): 13 | with app.app_context(): 14 | return TaskBase.__call__(self, *args, **kwargs) 15 | 16 | celery.Task = ContextTask 17 | return celery 18 | -------------------------------------------------------------------------------- /backend/app/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | from flask.json import JSONEncoder as BaseJSONEncoder 2 | 3 | 4 | class JSONEncoder(BaseJSONEncoder): 5 | """Custom :class:`JSONEncoder` which respects objects that include the 6 | :class:`JsonSerializer` mixin. 7 | """ 8 | 9 | def default(self, obj): 10 | if isinstance(obj, JsonSerializer): 11 | return obj.to_json() 12 | return super(JSONEncoder, self).default(obj) 13 | 14 | 15 | class JsonSerializer(object): 16 | """A mixin that can be used to mark a SQLAlchemy model class which 17 | implements a :func:`to_json` method. The :func:`to_json` method is used 18 | in conjuction with the custom :class:`JSONEncoder` class. By default this 19 | mixin will assume all properties of the SQLAlchemy model are to be visible 20 | in the JSON output. Extend this class to customize which properties are 21 | public, hidden or modified before being being passed to the JSON serializer. 22 | """ 23 | 24 | __json_public__ = None 25 | __json_hidden__ = None 26 | __json_modifiers__ = None 27 | 28 | def get_field_names(self): 29 | # for p in self.__mapper__.iterate_properties: 30 | for p in self._sa_class_manager.mapper.mapped_table.columns: 31 | yield p.key 32 | 33 | def to_json(self): 34 | field_names = self.get_field_names() 35 | 36 | public = self.__json_public__ or field_names 37 | hidden = self.__json_hidden__ or [] 38 | modifiers = self.__json_modifiers__ or dict() 39 | 40 | rv = dict() 41 | for key in public: 42 | rv[key] = getattr(self, key) 43 | for key, modifier in modifiers.items(): 44 | value = getattr(self, key) 45 | rv[key] = modifier(value, self) 46 | for key in hidden: 47 | rv.pop(key, None) 48 | 49 | return rv 50 | -------------------------------------------------------------------------------- /backend/app/helpers/decorators.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | -------------------------------------------------------------------------------- /backend/app/helpers/message.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | class MESSAGE(object): 5 | # COMMON ERROR 6 | INVALID_PARAMETER = 'please double check your parameters.' 7 | 8 | 9 | class CODE(object): 10 | # COMMON ERROR 11 | INVALID_PARAMETER = '1000' -------------------------------------------------------------------------------- /backend/app/helpers/response.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from flask import jsonify 4 | 5 | 6 | def response_ok(value=None, message='', code=-1): 7 | result = { 8 | 'status': 1, 9 | 'message': message, 10 | 'code': code 11 | } 12 | 13 | if len(message) > 0: 14 | result['message'] = message 15 | 16 | if value is not None: 17 | result.update({'data': value}) 18 | 19 | return jsonify(result) 20 | 21 | 22 | def response_error(message='', code=-1, status=0): 23 | result = { 24 | 'status': status, 25 | 'message': message, 26 | 'code': code 27 | } 28 | 29 | return jsonify(result) 30 | -------------------------------------------------------------------------------- /backend/app/helpers/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | 6 | 7 | def is_valid_email(email): 8 | if email is not None: 9 | email = email.lower() 10 | if re.match("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,6})$", email) != None: 11 | p = email.split('@') 12 | if p[1] is not None and p[1] not in ['spam4.me', 'pokemail.net', 'guerrillamailblock.com', 'guerrillamail.org', 'guerrillamail.net', 'guerrillamail.de', 'guerrillamail.com', 'guerrillamail.biz', 'grr.la', 'guerrillamail.info', 'sharklasers.com', 'yopmail.com', 'nopemail.me', 'maildrop.cc', 'proove.org', 'izolrom.ro', 'voltaer.com', 'shayzam.net', 'yevme.com', 'opayq.com', 'emailna.co', 'tuta.io', 'getnada.com', 'moruzza.com', 'mailhex.com', 'radiodale.com', 'datasoma.com', 'providier.com', 'cliptik.net', 'plutofox.com', 'lagify.com', 'khtyler.com', 'shayzam.ne', 'geroev.net', 'nando1.com', 'zdfpost.net', 'tempmail.ws']: 13 | return True 14 | return False 15 | -------------------------------------------------------------------------------- /backend/app/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/backend/app/models/__init__.py -------------------------------------------------------------------------------- /backend/app/routes/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import request, g 2 | from app import db 3 | from app.routes.product import product_routes 4 | 5 | 6 | def init_routes(app): 7 | 8 | @app.route('/') 9 | def hello(): 10 | return 'API' 11 | 12 | app.register_blueprint(product_routes, url_prefix='/product') 13 | -------------------------------------------------------------------------------- /backend/app/routes/product.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from flask import Blueprint, request, g 3 | from app.helpers.response import response_ok, response_error 4 | from app.helpers.message import MESSAGE, CODE 5 | 6 | import midtransclient 7 | import time 8 | 9 | product_routes = Blueprint('product', __name__) 10 | 11 | 12 | @product_routes.route('/', methods=['GET']) 13 | def product_list(): 14 | """ 15 | " get products 16 | " 17 | """ 18 | try: 19 | return response_ok([ 20 | { 21 | 'id': '1', 22 | 'name': 'Bell Local Helmet', 23 | 'desc': 'Orange Cycling Helmet', 24 | 'image_name': 'orange_helmet', 25 | 'price': '2000' 26 | }, 27 | { 28 | 'id': '2', 29 | 'name': 'Xiaomi Ninebot', 30 | 'desc': 'Black scooter helmet', 31 | 'image_name': 'unbranded_helmet', 32 | 'price': '2000' 33 | }, 34 | { 35 | 'id': '3', 36 | 'name': 'Unbranded Helmet', 37 | 'desc': 'Urban cycling helmet', 38 | 'image_name': 'xiaomi_helmet', 39 | 'price': '2000' 40 | } 41 | ]) 42 | except Exception as ex: 43 | return response_error(ex) 44 | 45 | 46 | @product_routes.route('/purchase', methods=['POST']) 47 | def purchase(): 48 | try: 49 | data = request.json 50 | if data is None: 51 | return response_error(MESSAGE.INVALID_PARAMETER, CODE.INVALID_PARAMETER) 52 | 53 | order_id = 'order_{}_{}'.format(data['id'], time.time()) 54 | gross_amount = data['price'] 55 | 56 | # Create Snap API instance 57 | snap = midtransclient.Snap( 58 | # Set to true if you want Production Environment (accept real transaction). 59 | is_production=False if g.ENV == 'DEV' else True, 60 | server_key=g.MIDTRANS_SERVER_KEY, 61 | client_key=g.MIDTRANS_CLIENT_KEY 62 | ) 63 | # Build API parameter 64 | param = { 65 | "transaction_details": { 66 | "order_id": order_id, 67 | "gross_amount": gross_amount 68 | }, 69 | } 70 | 71 | transaction = snap.create_transaction(param) 72 | transaction_token = transaction['token'] 73 | return response_ok(transaction_token) 74 | except Exception as ex: 75 | return response_error(ex.message) 76 | -------------------------------------------------------------------------------- /backend/app/settings-default.py: -------------------------------------------------------------------------------- 1 | import os 2 | from datetime import timedelta 3 | 4 | 5 | class BaseConfig(object): 6 | ENV = 'DEV' 7 | BASE_DIR = os.path.abspath(os.path.dirname(__file__)) 8 | BASE_URL = '' 9 | 10 | # SQLALCHEMY 11 | SQLALCHEMY_COMMIT_ON_TEARDOWN = True 12 | SQLALCHEMY_DATABASE_URI = 'mysql://root:@localhost/db?charset=utf8mb4' 13 | DATABASE_CONNECT_OPTIONS = {'charset': 'utf8mb4'} 14 | SQLALCHEMY_TRACK_MODIFICATIONS = True 15 | 16 | # JWT 17 | JWT_AUTH_USERNAME_KEY = 'email' 18 | JWT_ACCESS_TOKEN_EXPIRES = timedelta(days=60) 19 | JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=120) 20 | SECRET_KEY = '' 21 | 22 | REDIS_HOST = 'localhost' 23 | REDIS_PORT = 6379 24 | CELERY_BROKER_URL = 'redis://%s:%s/0' % (REDIS_HOST, REDIS_PORT) 25 | CELERY_RESULT_BACKEND = 'redis://%s:%s/0' % (REDIS_HOST, REDIS_PORT) 26 | MIDTRANS_SERVER_KEY = '' 27 | MIDTRANS_CLIENT_KEY = '' 28 | 29 | 30 | class DevelopmentConfig(BaseConfig): 31 | SQLALCHEMY_DATABASE_URI = 'mysql://root:@localhost/db?charset=utf8mb4' 32 | -------------------------------------------------------------------------------- /backend/app/settings.cfg: -------------------------------------------------------------------------------- 1 | SQLALCHEMY_DATABASE_URI = 'mysql://root:@localhost/db?charset=utf8mb4' -------------------------------------------------------------------------------- /backend/app/tasks/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from flask import Flask 3 | from app.factory import make_celery 4 | from app.core import db, configure_app 5 | 6 | app = Flask(__name__) 7 | # config app 8 | configure_app(app) 9 | 10 | # db 11 | db.init_app(app) 12 | 13 | # celery 14 | celery = make_celery(app) -------------------------------------------------------------------------------- /backend/logging.conf: -------------------------------------------------------------------------------- 1 | version: 1 2 | formatters: 3 | simple: 4 | format: '[%(asctime)s - %(filename)s:%(lineno)s - %(funcName)s] %(message)s' 5 | handlers: 6 | console: 7 | class: logging.StreamHandler 8 | level: DEBUG 9 | formatter: simple 10 | stream: ext://sys.stdout 11 | file: 12 | class: logging.FileHandler 13 | level: DEBUG 14 | formatter: simple 15 | filename: logs/debug.log 16 | loggers: 17 | console: 18 | level: DEBUG 19 | handlers: [console] 20 | propagate: no 21 | file: 22 | level: DEBUG 23 | handlers: [file] 24 | propagate: no 25 | root: 26 | level: DEBUG 27 | handlers: [console] -------------------------------------------------------------------------------- /backend/logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/backend/logs/.gitkeep -------------------------------------------------------------------------------- /backend/manage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | manage 4 | """ 5 | from flask_script import Manager 6 | from flask_migrate import Migrate, MigrateCommand 7 | from app import app, db 8 | 9 | migrate = Migrate(app, db) 10 | 11 | manager = Manager(app) 12 | manager.add_command('db', MigrateCommand) 13 | 14 | if __name__ == '__main__': 15 | manager.run() 16 | -------------------------------------------------------------------------------- /backend/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /backend/migrations/env.py: -------------------------------------------------------------------------------- 1 | from flask import current_app 2 | from logging.config import fileConfig 3 | 4 | from sqlalchemy import engine_from_config 5 | from sqlalchemy import pool 6 | 7 | from alembic import context 8 | 9 | import logging 10 | # this is the Alembic Config object, which provides 11 | # access to the values within the .ini file in use. 12 | config = context.config 13 | 14 | # Interpret the config file for Python logging. 15 | # This line sets up loggers basically. 16 | fileConfig(config.config_file_name) 17 | logger = logging.getLogger('alembic.env') 18 | 19 | # add your model's MetaData object here 20 | # for 'autogenerate' support 21 | # from myapp import mymodel 22 | # target_metadata = mymodel.Base.metadata 23 | config.set_main_option('sqlalchemy.url', 24 | current_app.config.get('SQLALCHEMY_DATABASE_URI')) 25 | target_metadata = current_app.extensions['migrate'].db.metadata 26 | 27 | 28 | # other values from the config, defined by the needs of env.py, 29 | # can be acquired: 30 | # my_important_option = config.get_main_option("my_important_option") 31 | # ... etc. 32 | 33 | 34 | def run_migrations_offline(): 35 | """Run migrations in 'offline' mode. 36 | 37 | This configures the context with just a URL 38 | and not an Engine, though an Engine is acceptable 39 | here as well. By skipping the Engine creation 40 | we don't even need a DBAPI to be available. 41 | 42 | Calls to context.execute() here emit the given string to the 43 | script output. 44 | 45 | """ 46 | url = config.get_main_option("sqlalchemy.url") 47 | context.configure( 48 | url=url, 49 | target_metadata=target_metadata, 50 | literal_binds=True, 51 | dialect_opts={"paramstyle": "named"}, 52 | ) 53 | 54 | with context.begin_transaction(): 55 | context.run_migrations() 56 | 57 | 58 | def run_migrations_online(): 59 | """Run migrations in 'online' mode. 60 | 61 | In this scenario we need to create an Engine 62 | and associate a connection with the context. 63 | 64 | """ 65 | connectable = engine_from_config( 66 | config.get_section(config.config_ini_section), 67 | prefix="sqlalchemy.", 68 | poolclass=pool.NullPool, 69 | ) 70 | 71 | with connectable.connect() as connection: 72 | context.configure( 73 | connection=connection, target_metadata=target_metadata 74 | ) 75 | 76 | with context.begin_transaction(): 77 | context.run_migrations() 78 | 79 | 80 | if context.is_offline_mode(): 81 | run_migrations_offline() 82 | else: 83 | run_migrations_online() 84 | -------------------------------------------------------------------------------- /backend/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | alembic==1.4.3 2 | certifi==2020.11.8 3 | chardet==3.0.4 4 | click==7.1.2 5 | Flask==1.1.2 6 | Flask-Cors==3.0.9 7 | Flask-JWT-Extended==3.25.0 8 | Flask-Migrate==2.5.3 9 | Flask-Script==2.0.6 10 | Flask-SQLAlchemy==2.4.4 11 | idna==2.10 12 | itsdangerous==1.1.0 13 | Jinja2==2.11.2 14 | Mako==1.1.3 15 | MarkupSafe==1.1.1 16 | midtransclient==1.1.1 17 | mysqlclient==2.0.1 18 | PyJWT==1.7.1 19 | python-dateutil==2.8.1 20 | python-editor==1.0.4 21 | PyYAML==5.3.1 22 | requests==2.25.0 23 | six==1.15.0 24 | SQLAlchemy==1.3.20 25 | urllib3==1.26.2 26 | Werkzeug==1.0.1 27 | -------------------------------------------------------------------------------- /backend/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python manage.py db upgrade 3 | celery -A app.tasks:celery worker --loglevel=info & python wsgi.py -------------------------------------------------------------------------------- /backend/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/backend/tests/__init__.py -------------------------------------------------------------------------------- /backend/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | wsgi-file=/backend/wsgi.py -------------------------------------------------------------------------------- /backend/wsgi.py: -------------------------------------------------------------------------------- 1 | from werkzeug.serving import run_simple 2 | from app import app as application 3 | 4 | if __name__ == "__main__": 5 | run_simple('0.0.0.0', 5000, application, use_reloader=True, 6 | use_debugger=True, threaded=True) 7 | -------------------------------------------------------------------------------- /flutter_midtrans/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | -------------------------------------------------------------------------------- /flutter_midtrans/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8874f21e79d7ec66d0457c7ab338348e31b17f1d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /flutter_midtrans/README.md: -------------------------------------------------------------------------------- 1 | # flutter_midtrans 2 | 3 | A new Flutter project. 4 | 5 | 6 | ## How to run 7 | 8 | - replace `MIDTRANS_CLIENT_KEY` with your current key in lib/config/config.dart 9 | - flutter run 10 | 11 | ## Getting Started 12 | 13 | This project is a starting point for a Flutter application. 14 | 15 | A few resources to get you started if this is your first Flutter project: 16 | 17 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 18 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 19 | 20 | For help getting started with Flutter, view our 21 | [online documentation](https://flutter.dev/docs), which offers tutorials, 22 | samples, guidance on mobile development, and a full API reference. 23 | -------------------------------------------------------------------------------- /flutter_midtrans/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /flutter_midtrans/android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android______ 4 | Project android______ created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | 19 | 1606813395044 20 | 21 | 30 22 | 23 | org.eclipse.core.resources.regexFilterMatcher 24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /flutter_midtrans/android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments= 2 | auto.sync=false 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) 5 | connection.project.dir= 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=/Library/Java/JavaVirtualMachines/jdk-11.0.8.jdk/Contents/Home 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /flutter_midtrans/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 29 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.flutter_midtrans" 42 | minSdkVersion 16 43 | targetSdkVersion 29 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/kotlin/com/example/flutter_midtrans/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_midtrans 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /flutter_midtrans/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /flutter_midtrans/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /flutter_midtrans/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /flutter_midtrans/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /flutter_midtrans/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /flutter_midtrans/assets/orange_helmet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/assets/orange_helmet.png -------------------------------------------------------------------------------- /flutter_midtrans/assets/top_bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | BG 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /flutter_midtrans/assets/unbranded_helmet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/assets/unbranded_helmet.png -------------------------------------------------------------------------------- /flutter_midtrans/assets/xiaomi_helmet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/assets/xiaomi_helmet.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - fluttertoast (0.0.2): 4 | - Flutter 5 | - Toast 6 | - shared_preferences (0.0.1): 7 | - Flutter 8 | - Toast (4.0.0) 9 | - webview_flutter (0.0.1): 10 | - Flutter 11 | 12 | DEPENDENCIES: 13 | - Flutter (from `Flutter`) 14 | - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) 15 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 16 | - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) 17 | 18 | SPEC REPOS: 19 | trunk: 20 | - Toast 21 | 22 | EXTERNAL SOURCES: 23 | Flutter: 24 | :path: Flutter 25 | fluttertoast: 26 | :path: ".symlinks/plugins/fluttertoast/ios" 27 | shared_preferences: 28 | :path: ".symlinks/plugins/shared_preferences/ios" 29 | webview_flutter: 30 | :path: ".symlinks/plugins/webview_flutter/ios" 31 | 32 | SPEC CHECKSUMS: 33 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 34 | fluttertoast: 6122fa75143e992b1d3470f61000f591a798cc58 35 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d 36 | Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 37 | webview_flutter: d2b4d6c66968ad042ad94cbb791f5b72b4678a96 38 | 39 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 40 | 41 | COCOAPODS: 1.10.0 42 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | C44B937FEA379B9C8FF60CCB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091CA0A4CB986CD86297CCFE /* Pods_Runner.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 091CA0A4CB986CD86297CCFE /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 35 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 36 | 2DF431A7A182E94BF9D00A3B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 37 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 38 | 6092F1757FCFE735E98D9927 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 39 | 6D6BADD0787B3F636FD0DE88 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | C44B937FEA379B9C8FF60CCB /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 0DE94FEE70553B5AB00AEFF0 /* Frameworks */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 091CA0A4CB986CD86297CCFE /* Pods_Runner.framework */, 68 | ); 69 | name = Frameworks; 70 | sourceTree = ""; 71 | }; 72 | 9740EEB11CF90186004384FC /* Flutter */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 76 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 77 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 78 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 79 | ); 80 | name = Flutter; 81 | sourceTree = ""; 82 | }; 83 | 97C146E51CF9000F007C117D = { 84 | isa = PBXGroup; 85 | children = ( 86 | 9740EEB11CF90186004384FC /* Flutter */, 87 | 97C146F01CF9000F007C117D /* Runner */, 88 | 97C146EF1CF9000F007C117D /* Products */, 89 | D87D79BBEE47D044ECD3C403 /* Pods */, 90 | 0DE94FEE70553B5AB00AEFF0 /* Frameworks */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 97C146EF1CF9000F007C117D /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 97C146EE1CF9000F007C117D /* Runner.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 97C146F01CF9000F007C117D /* Runner */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 106 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 107 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 108 | 97C147021CF9000F007C117D /* Info.plist */, 109 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 110 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 111 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 112 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 113 | ); 114 | path = Runner; 115 | sourceTree = ""; 116 | }; 117 | D87D79BBEE47D044ECD3C403 /* Pods */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 6D6BADD0787B3F636FD0DE88 /* Pods-Runner.debug.xcconfig */, 121 | 6092F1757FCFE735E98D9927 /* Pods-Runner.release.xcconfig */, 122 | 2DF431A7A182E94BF9D00A3B /* Pods-Runner.profile.xcconfig */, 123 | ); 124 | name = Pods; 125 | path = Pods; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXNativeTarget section */ 131 | 97C146ED1CF9000F007C117D /* Runner */ = { 132 | isa = PBXNativeTarget; 133 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 134 | buildPhases = ( 135 | 7ACE97A630119C8BF59FE6D4 /* [CP] Check Pods Manifest.lock */, 136 | 9740EEB61CF901F6004384FC /* Run Script */, 137 | 97C146EA1CF9000F007C117D /* Sources */, 138 | 97C146EB1CF9000F007C117D /* Frameworks */, 139 | 97C146EC1CF9000F007C117D /* Resources */, 140 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 141 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 142 | 57155D62549E8E074A75741A /* [CP] Embed Pods Frameworks */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = Runner; 149 | productName = Runner; 150 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | 97C146E61CF9000F007C117D /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 1020; 160 | ORGANIZATIONNAME = ""; 161 | TargetAttributes = { 162 | 97C146ED1CF9000F007C117D = { 163 | CreatedOnToolsVersion = 7.3.1; 164 | LastSwiftMigration = 1100; 165 | }; 166 | }; 167 | }; 168 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 169 | compatibilityVersion = "Xcode 9.3"; 170 | developmentRegion = en; 171 | hasScannedForEncodings = 0; 172 | knownRegions = ( 173 | en, 174 | Base, 175 | ); 176 | mainGroup = 97C146E51CF9000F007C117D; 177 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 178 | projectDirPath = ""; 179 | projectRoot = ""; 180 | targets = ( 181 | 97C146ED1CF9000F007C117D /* Runner */, 182 | ); 183 | }; 184 | /* End PBXProject section */ 185 | 186 | /* Begin PBXResourcesBuildPhase section */ 187 | 97C146EC1CF9000F007C117D /* Resources */ = { 188 | isa = PBXResourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 192 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 193 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "Thin Binary"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 214 | }; 215 | 57155D62549E8E074A75741A /* [CP] Embed Pods Frameworks */ = { 216 | isa = PBXShellScriptBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | inputFileListPaths = ( 221 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 222 | ); 223 | name = "[CP] Embed Pods Frameworks"; 224 | outputFileListPaths = ( 225 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | shellPath = /bin/sh; 229 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 230 | showEnvVarsInLog = 0; 231 | }; 232 | 7ACE97A630119C8BF59FE6D4 /* [CP] Check Pods Manifest.lock */ = { 233 | isa = PBXShellScriptBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | ); 237 | inputFileListPaths = ( 238 | ); 239 | inputPaths = ( 240 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 241 | "${PODS_ROOT}/Manifest.lock", 242 | ); 243 | name = "[CP] Check Pods Manifest.lock"; 244 | outputFileListPaths = ( 245 | ); 246 | outputPaths = ( 247 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | shellPath = /bin/sh; 251 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 252 | showEnvVarsInLog = 0; 253 | }; 254 | 9740EEB61CF901F6004384FC /* Run Script */ = { 255 | isa = PBXShellScriptBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | ); 259 | inputPaths = ( 260 | ); 261 | name = "Run Script"; 262 | outputPaths = ( 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | shellPath = /bin/sh; 266 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 267 | }; 268 | /* End PBXShellScriptBuildPhase section */ 269 | 270 | /* Begin PBXSourcesBuildPhase section */ 271 | 97C146EA1CF9000F007C117D /* Sources */ = { 272 | isa = PBXSourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 276 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | /* End PBXSourcesBuildPhase section */ 281 | 282 | /* Begin PBXVariantGroup section */ 283 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 284 | isa = PBXVariantGroup; 285 | children = ( 286 | 97C146FB1CF9000F007C117D /* Base */, 287 | ); 288 | name = Main.storyboard; 289 | sourceTree = ""; 290 | }; 291 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 292 | isa = PBXVariantGroup; 293 | children = ( 294 | 97C147001CF9000F007C117D /* Base */, 295 | ); 296 | name = LaunchScreen.storyboard; 297 | sourceTree = ""; 298 | }; 299 | /* End PBXVariantGroup section */ 300 | 301 | /* Begin XCBuildConfiguration section */ 302 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 333 | ENABLE_NS_ASSERTIONS = NO; 334 | ENABLE_STRICT_OBJC_MSGSEND = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_NO_COMMON_BLOCKS = YES; 337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 339 | GCC_WARN_UNDECLARED_SELECTOR = YES; 340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 341 | GCC_WARN_UNUSED_FUNCTION = YES; 342 | GCC_WARN_UNUSED_VARIABLE = YES; 343 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 344 | MTL_ENABLE_DEBUG_INFO = NO; 345 | SDKROOT = iphoneos; 346 | SUPPORTED_PLATFORMS = iphoneos; 347 | TARGETED_DEVICE_FAMILY = "1,2"; 348 | VALIDATE_PRODUCT = YES; 349 | }; 350 | name = Profile; 351 | }; 352 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 353 | isa = XCBuildConfiguration; 354 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 355 | buildSettings = { 356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 357 | CLANG_ENABLE_MODULES = YES; 358 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 359 | ENABLE_BITCODE = NO; 360 | FRAMEWORK_SEARCH_PATHS = ( 361 | "$(inherited)", 362 | "$(PROJECT_DIR)/Flutter", 363 | ); 364 | INFOPLIST_FILE = Runner/Info.plist; 365 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 366 | LIBRARY_SEARCH_PATHS = ( 367 | "$(inherited)", 368 | "$(PROJECT_DIR)/Flutter", 369 | ); 370 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterMidtrans; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 373 | SWIFT_VERSION = 5.0; 374 | VERSIONING_SYSTEM = "apple-generic"; 375 | }; 376 | name = Profile; 377 | }; 378 | 97C147031CF9000F007C117D /* Debug */ = { 379 | isa = XCBuildConfiguration; 380 | buildSettings = { 381 | ALWAYS_SEARCH_USER_PATHS = NO; 382 | CLANG_ANALYZER_NONNULL = YES; 383 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 384 | CLANG_CXX_LIBRARY = "libc++"; 385 | CLANG_ENABLE_MODULES = YES; 386 | CLANG_ENABLE_OBJC_ARC = YES; 387 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 388 | CLANG_WARN_BOOL_CONVERSION = YES; 389 | CLANG_WARN_COMMA = YES; 390 | CLANG_WARN_CONSTANT_CONVERSION = YES; 391 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 392 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 393 | CLANG_WARN_EMPTY_BODY = YES; 394 | CLANG_WARN_ENUM_CONVERSION = YES; 395 | CLANG_WARN_INFINITE_RECURSION = YES; 396 | CLANG_WARN_INT_CONVERSION = YES; 397 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 398 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 399 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 400 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 401 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 402 | CLANG_WARN_STRICT_PROTOTYPES = YES; 403 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 404 | CLANG_WARN_UNREACHABLE_CODE = YES; 405 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 406 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 407 | COPY_PHASE_STRIP = NO; 408 | DEBUG_INFORMATION_FORMAT = dwarf; 409 | ENABLE_STRICT_OBJC_MSGSEND = YES; 410 | ENABLE_TESTABILITY = YES; 411 | GCC_C_LANGUAGE_STANDARD = gnu99; 412 | GCC_DYNAMIC_NO_PIC = NO; 413 | GCC_NO_COMMON_BLOCKS = YES; 414 | GCC_OPTIMIZATION_LEVEL = 0; 415 | GCC_PREPROCESSOR_DEFINITIONS = ( 416 | "DEBUG=1", 417 | "$(inherited)", 418 | ); 419 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 420 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 421 | GCC_WARN_UNDECLARED_SELECTOR = YES; 422 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 423 | GCC_WARN_UNUSED_FUNCTION = YES; 424 | GCC_WARN_UNUSED_VARIABLE = YES; 425 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 426 | MTL_ENABLE_DEBUG_INFO = YES; 427 | ONLY_ACTIVE_ARCH = YES; 428 | SDKROOT = iphoneos; 429 | TARGETED_DEVICE_FAMILY = "1,2"; 430 | }; 431 | name = Debug; 432 | }; 433 | 97C147041CF9000F007C117D /* Release */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | ALWAYS_SEARCH_USER_PATHS = NO; 437 | CLANG_ANALYZER_NONNULL = YES; 438 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 439 | CLANG_CXX_LIBRARY = "libc++"; 440 | CLANG_ENABLE_MODULES = YES; 441 | CLANG_ENABLE_OBJC_ARC = YES; 442 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 443 | CLANG_WARN_BOOL_CONVERSION = YES; 444 | CLANG_WARN_COMMA = YES; 445 | CLANG_WARN_CONSTANT_CONVERSION = YES; 446 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 447 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 448 | CLANG_WARN_EMPTY_BODY = YES; 449 | CLANG_WARN_ENUM_CONVERSION = YES; 450 | CLANG_WARN_INFINITE_RECURSION = YES; 451 | CLANG_WARN_INT_CONVERSION = YES; 452 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 453 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 454 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 455 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 456 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 457 | CLANG_WARN_STRICT_PROTOTYPES = YES; 458 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 459 | CLANG_WARN_UNREACHABLE_CODE = YES; 460 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 461 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 462 | COPY_PHASE_STRIP = NO; 463 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 464 | ENABLE_NS_ASSERTIONS = NO; 465 | ENABLE_STRICT_OBJC_MSGSEND = YES; 466 | GCC_C_LANGUAGE_STANDARD = gnu99; 467 | GCC_NO_COMMON_BLOCKS = YES; 468 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 469 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 470 | GCC_WARN_UNDECLARED_SELECTOR = YES; 471 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 472 | GCC_WARN_UNUSED_FUNCTION = YES; 473 | GCC_WARN_UNUSED_VARIABLE = YES; 474 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 475 | MTL_ENABLE_DEBUG_INFO = NO; 476 | SDKROOT = iphoneos; 477 | SUPPORTED_PLATFORMS = iphoneos; 478 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 479 | TARGETED_DEVICE_FAMILY = "1,2"; 480 | VALIDATE_PRODUCT = YES; 481 | }; 482 | name = Release; 483 | }; 484 | 97C147061CF9000F007C117D /* Debug */ = { 485 | isa = XCBuildConfiguration; 486 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 487 | buildSettings = { 488 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 489 | CLANG_ENABLE_MODULES = YES; 490 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 491 | ENABLE_BITCODE = NO; 492 | FRAMEWORK_SEARCH_PATHS = ( 493 | "$(inherited)", 494 | "$(PROJECT_DIR)/Flutter", 495 | ); 496 | INFOPLIST_FILE = Runner/Info.plist; 497 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 498 | LIBRARY_SEARCH_PATHS = ( 499 | "$(inherited)", 500 | "$(PROJECT_DIR)/Flutter", 501 | ); 502 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterMidtrans; 503 | PRODUCT_NAME = "$(TARGET_NAME)"; 504 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 505 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 506 | SWIFT_VERSION = 5.0; 507 | VERSIONING_SYSTEM = "apple-generic"; 508 | }; 509 | name = Debug; 510 | }; 511 | 97C147071CF9000F007C117D /* Release */ = { 512 | isa = XCBuildConfiguration; 513 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 514 | buildSettings = { 515 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 516 | CLANG_ENABLE_MODULES = YES; 517 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 518 | ENABLE_BITCODE = NO; 519 | FRAMEWORK_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "$(PROJECT_DIR)/Flutter", 522 | ); 523 | INFOPLIST_FILE = Runner/Info.plist; 524 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 525 | LIBRARY_SEARCH_PATHS = ( 526 | "$(inherited)", 527 | "$(PROJECT_DIR)/Flutter", 528 | ); 529 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterMidtrans; 530 | PRODUCT_NAME = "$(TARGET_NAME)"; 531 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 532 | SWIFT_VERSION = 5.0; 533 | VERSIONING_SYSTEM = "apple-generic"; 534 | }; 535 | name = Release; 536 | }; 537 | /* End XCBuildConfiguration section */ 538 | 539 | /* Begin XCConfigurationList section */ 540 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 541 | isa = XCConfigurationList; 542 | buildConfigurations = ( 543 | 97C147031CF9000F007C117D /* Debug */, 544 | 97C147041CF9000F007C117D /* Release */, 545 | 249021D3217E4FDB00AE95B9 /* Profile */, 546 | ); 547 | defaultConfigurationIsVisible = 0; 548 | defaultConfigurationName = Release; 549 | }; 550 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 551 | isa = XCConfigurationList; 552 | buildConfigurations = ( 553 | 97C147061CF9000F007C117D /* Debug */, 554 | 97C147071CF9000F007C117D /* Release */, 555 | 249021D4217E4FDB00AE95B9 /* Profile */, 556 | ); 557 | defaultConfigurationIsVisible = 0; 558 | defaultConfigurationName = Release; 559 | }; 560 | /* End XCConfigurationList section */ 561 | }; 562 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 563 | } 564 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/flutter_midtrans/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_midtrans 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /flutter_midtrans/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/blocs/product/product_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:flutter_midtrans/blocs/product/product_state.dart'; 3 | import 'package:flutter_midtrans/data/models/product.dart'; 4 | import 'package:flutter_midtrans/repositories/product_repo.dart'; 5 | 6 | class ProductCubit extends Cubit { 7 | final ProductRepository productRepo; 8 | 9 | ProductCubit(this.productRepo) : super(ProductInitial()); 10 | 11 | Future loadProducts() async { 12 | emit(ProductLoading()); 13 | var products = await productRepo.loadProducts(); 14 | emit(ProductLoadedSuccess(products: products.products)); 15 | } 16 | 17 | Future purchase(Product product) async { 18 | emit(ProductLoading()); 19 | var response = await productRepo.buyProduct(product: product); 20 | emit(ProductCreateOrderSuccess(transactionToken: response.data)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/blocs/product/product_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_midtrans/data/models/product.dart'; 4 | 5 | class ProductState extends Equatable { 6 | final List props; 7 | const ProductState([this.props]); 8 | } 9 | 10 | class ProductInitial extends ProductState { 11 | @override 12 | String toString() => 'ProductInitial'; 13 | } 14 | 15 | class ProductLoading extends ProductState { 16 | @override 17 | String toString() => 'ProductLoading'; 18 | } 19 | 20 | class ProductLoadedSuccess extends ProductState { 21 | final List products; 22 | ProductLoadedSuccess({@required this.products}) : super([products]); 23 | 24 | @override 25 | String toString() => 'ProductLoadedSuccess'; 26 | } 27 | 28 | class ProductCreateOrderSuccess extends ProductState { 29 | final String transactionToken; 30 | ProductCreateOrderSuccess({@required this.transactionToken}) : super([transactionToken]); 31 | 32 | @override 33 | String toString() => 'ProductCreateOrderSuccess'; 34 | } 35 | 36 | class ProductFailure extends ProductState { 37 | final String error; 38 | ProductFailure({@required this.error}) : super([error]); 39 | 40 | @override 41 | String toString() => 'ProductFailure { error: $error }'; 42 | } 43 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/config/config.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_midtrans/helpers/theme/themes.dart'; 2 | 3 | enum Environment { Local, Dev, Prod } 4 | 5 | class App { 6 | // This contains global variables. We should avoid using this in case you have special reason. 7 | static Themes theme; 8 | } 9 | 10 | class Config { 11 | static Map _config; 12 | 13 | static void setEnvironment(Environment env) { 14 | switch (env) { 15 | case Environment.Local: 16 | _config = _ConfigMap.localConfig; 17 | break; 18 | case Environment.Dev: 19 | _config = _ConfigMap.devConfig; 20 | break; 21 | case Environment.Prod: 22 | _config = _ConfigMap.prodConfig; 23 | break; 24 | } 25 | } 26 | 27 | static String getEnvironment() { 28 | if (_config == _ConfigMap.prodConfig) { 29 | return "prod"; 30 | } else if (_config == _ConfigMap.devConfig) { 31 | return "dev"; 32 | } else { 33 | return "local"; 34 | } 35 | } 36 | 37 | static bool isProd() => _config == _ConfigMap.prodConfig; 38 | 39 | static bool isLocal() => _config == _ConfigMap.localConfig; 40 | 41 | static get baseURL { 42 | return _config[_ConfigMap.BASE_URL]; 43 | } 44 | 45 | static get midtransURL { 46 | return _config[_ConfigMap.MIDTRANS_URL]; 47 | } 48 | 49 | static get midtransClientKey { 50 | return _config[_ConfigMap.MIDTRANS_CLIENT_KEY]; 51 | } 52 | } 53 | 54 | class _ConfigMap { 55 | static const BASE_URL = "BASE_URL"; 56 | static const MIDTRANS_URL = "MIDTRANS_URL"; 57 | static const MIDTRANS_CLIENT_KEY = "MIDTRANS_CLIENT_KEY"; 58 | 59 | static Map localConfig = { 60 | BASE_URL: "http://localhost:5000", 61 | MIDTRANS_URL: "https://app.sandbox.midtrans.com/snap/snap.js", 62 | MIDTRANS_CLIENT_KEY: "" 63 | }; 64 | 65 | static Map devConfig = { 66 | BASE_URL: "http://localhost:5000", 67 | MIDTRANS_URL: "https://app.sandbox.midtrans.com/snap/snap.js", 68 | MIDTRANS_CLIENT_KEY: "" 69 | }; 70 | 71 | static Map prodConfig = { 72 | BASE_URL: "http://localhost:5000", 73 | MIDTRANS_URL: "https://app.sandbox.midtrans.com/snap/snap.js", 74 | MIDTRANS_CLIENT_KEY: "" 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/config/main_dev.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_midtrans/config/config.dart'; 2 | import 'package:flutter_midtrans/main_configured.dart'; 3 | 4 | void main() { 5 | Config.setEnvironment(Environment.Dev); 6 | mainDelegate(); 7 | } 8 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/config/main_local.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_midtrans/config/config.dart'; 2 | import 'package:flutter_midtrans/main_configured.dart'; 3 | 4 | void main() { 5 | Config.setEnvironment(Environment.Local); 6 | mainDelegate(); 7 | } 8 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/config/main_prod.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_midtrans/config/config.dart'; 2 | import 'package:flutter_midtrans/main_configured.dart'; 3 | 4 | void main() { 5 | Config.setEnvironment(Environment.Prod); 6 | mainDelegate(); 7 | } 8 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/data/local/storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_midtrans/helpers/theme/theme_provider.dart'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | const THEME_MODE = 'THEME_MODE'; 6 | 7 | class Storage { 8 | static final Storage _singleton = new Storage._internal(); 9 | SharedPreferences _prefs; 10 | 11 | factory Storage() { 12 | return _singleton; 13 | } 14 | 15 | Storage._internal(); 16 | 17 | Future init() async { 18 | if (_prefs == null) { 19 | _prefs = await SharedPreferences.getInstance(); 20 | } 21 | } 22 | 23 | registerTestPreference(SharedPreferences sharedPreferences) { 24 | _prefs = sharedPreferences; 25 | } 26 | 27 | ThemeMode getThemeMode() { 28 | ThemeEnum themeEnum = ThemeEnum.getThemeByThemeMode(_prefs.getString(THEME_MODE)); 29 | return themeEnum.themeMode; 30 | } 31 | 32 | setThemeMode(ThemeMode themeMode) { 33 | _prefs.setString(THEME_MODE, themeMode.toString()); 34 | } 35 | 36 | Future saveBoolean(String key, bool value) async { 37 | _prefs.setBool(key, value); 38 | } 39 | 40 | Future getBoolean(String key) async { 41 | return _prefs.getBool(key) ?? false; 42 | } 43 | 44 | Future saveString(String key, String value) async { 45 | _prefs.setString(key, value); 46 | } 47 | 48 | Future remove(String key) async { 49 | _prefs.remove(key); 50 | } 51 | 52 | String getString(String key) { 53 | return _prefs.getString(key) ?? ''; 54 | } 55 | 56 | Future saveInt(String key, int value) async { 57 | _prefs.setInt(key, value); 58 | } 59 | 60 | int getInt(String key) { 61 | return _prefs.getInt(key) ?? -1; 62 | } 63 | 64 | Future saveDouble(String key, double value) async { 65 | _prefs.setDouble(key, value); 66 | } 67 | 68 | Future getDouble(String key) async { 69 | return _prefs.getDouble(key) ?? 0; 70 | } 71 | } 72 | 73 | final storage = Storage(); 74 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/data/models/product.dart: -------------------------------------------------------------------------------- 1 | class Product { 2 | String productId; 3 | String productName; 4 | String productDesc; 5 | String productImageName; 6 | String productPrice; 7 | 8 | static const PRODUCT_ID = "id"; 9 | static const PRODUCT_NAME = "name"; 10 | static const PRODUCT_DESC = "desc"; 11 | static const PRODUCT_IMAGE_NAME = "image_name"; 12 | static const PRODUCT_PRICE = "price"; 13 | 14 | Product({this.productId, this.productName, this.productDesc, this.productImageName, this.productPrice}); 15 | 16 | Map toMap() { 17 | var map = { 18 | PRODUCT_ID: productId, 19 | PRODUCT_NAME: productName, 20 | PRODUCT_DESC: productDesc, 21 | PRODUCT_IMAGE_NAME: productImageName, 22 | PRODUCT_PRICE: productPrice, 23 | }; 24 | return map; 25 | } 26 | 27 | Product.fromMap(Map map) { 28 | productId = map[PRODUCT_ID] as String; 29 | productName = map[PRODUCT_NAME] as String; 30 | productDesc = map[PRODUCT_DESC] as String; 31 | productImageName = map[PRODUCT_IMAGE_NAME] as String; 32 | productPrice = map[PRODUCT_PRICE] as String; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/api_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter_midtrans/helpers/error_codes.dart'; 3 | 4 | Future handleResponse(Future request) async { 5 | try { 6 | Response response = await request; 7 | if (response == null) return APIResponse(success: false); 8 | 9 | var data = response.data; 10 | var success = data != null && data["status"] == 1; 11 | return APIResponse(success: success, data: data); 12 | } on DioError catch (error) { 13 | throw error; 14 | } 15 | } 16 | 17 | class APIResponse { 18 | final bool success; 19 | final dynamic data; 20 | 21 | APIResponse({this.success = false, this.data}); 22 | } 23 | 24 | class APIUnauthorizedException implements Exception { 25 | String message; 26 | APIUnauthorizedException(this.message); 27 | } 28 | 29 | class APIUnknownError implements Exception { 30 | String message; 31 | APIUnknownError(this.message); 32 | 33 | @override 34 | String toString() { 35 | return message; 36 | } 37 | } 38 | 39 | class APIErrorWithCode implements Exception { 40 | final String defaultMessage; 41 | final num errorCode; 42 | 43 | APIErrorWithCode(this.errorCode, {this.defaultMessage}); 44 | 45 | get message { 46 | var msg = ErrorCodes.errorCodes[errorCode]; 47 | if (msg == null) return defaultMessage ?? "Unknown error code: $errorCode"; 48 | return msg; 49 | } 50 | 51 | @override 52 | String toString() { 53 | return message; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/error_codes.dart: -------------------------------------------------------------------------------- 1 | abstract class ErrorCodes { 2 | static final Map errorCodes = { 3 | -1: '', 4 | 214: '', 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/logger/log_filter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_midtrans/config/config.dart'; 2 | import 'package:logger/logger.dart'; 3 | 4 | class SimpleLogFilter extends LogFilter { 5 | @override 6 | bool shouldLog(LogEvent event) { 7 | if (Config.isProd()) return false; 8 | return true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/logger/log_printer.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | class SimpleLogPrinter extends LogPrinter { 4 | final LogPrinter _realPrinter; 5 | Map _prefixMap; 6 | 7 | SimpleLogPrinter(this._realPrinter, {debug, verbose, wtf, info, warning, error, nothing}) 8 | : super() { 9 | _prefixMap = { 10 | Level.debug: debug ?? ' DEBUG ', 11 | Level.verbose: verbose ?? 'VERBOSE ', 12 | Level.wtf: wtf ?? ' WTF ', 13 | Level.info: info ?? ' INFO ', 14 | Level.warning: warning ?? 'WARNING ', 15 | Level.error: error ?? ' ERROR ', 16 | Level.nothing: nothing ?? 'NOTHING', 17 | }; 18 | } 19 | 20 | @override 21 | List log(LogEvent event) { 22 | return _realPrinter.log(event).map((s) => '${_prefixMap[event.level]}$s').toList(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/logger/tokoin_log.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'log_filter.dart'; 3 | import 'log_printer.dart'; 4 | 5 | Logger getLogger() { 6 | return Logger( 7 | filter: SimpleLogFilter(), 8 | printer: SimpleLogPrinter( 9 | PrettyPrinter( 10 | methodCount: 2, // number of method calls to be displayed 11 | errorMethodCount: 8, // number of method calls if stacktrace is provided 12 | lineLength: 120, // width of the output 13 | colors: false, // Colorful log messages 14 | printEmojis: false, 15 | printTime: false, 16 | ), 17 | ), 18 | ); 19 | } 20 | 21 | final log = getLogger(); 22 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/theme/theme_dark.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'themes.dart'; 4 | 5 | final darkTheme = Themes( 6 | colors: Colours( 7 | appBar: Color(0xFFFFFFFF), 8 | border: Color(0xFFD8D8D8), 9 | bottomBar: Color(0xFFFFFFFF), 10 | modelSheet: Color(0xFFFFFFF), 11 | row: Color(0x52D8D8D8), 12 | banner: Color(0x4DD8D8D8), 13 | card: Color(0xFFFFFFFF), 14 | button3: Color(0xFF21409A), 15 | button2: Color(0xFF000000), 16 | button1: Color(0xFF4D4084), 17 | button4: Color(0xFFF0F0F0), 18 | primary: Color(0xFF000000), 19 | background: Color(0xFFFFFFFF), 20 | selectedItemBackground: Color(0x274D4084), 21 | text1: Color(0x274D4084), 22 | text2: Color(0xFF21409A), 23 | text3: Color(0xFF4D4084), 24 | text4: Color(0xC98D8D8D), 25 | text5: Color(0xFFB00020), 26 | text6: Color(0xFFFFFFFF), 27 | text7: Color(0xFF787878), 28 | text8: Color(0xFF3B3B3B), 29 | text9: Color(0xFF383838), 30 | text10: Color(0xFF515151), 31 | text11: Color(0xFF080808), 32 | text12: Color(0xFF00B203), 33 | text13: Color(0xFF292929), 34 | error: Color(0xFFC52D2D), 35 | gradientStart1: Color(0xFF645AFF), 36 | gradientEnd1: Color(0xFFA573FF), 37 | disabled: Color(0x4D21409A), 38 | dot: Color(0x50FFFFFF), 39 | divider: Color(0xAA979797), 40 | pending: Color(0xFFFFAE42), 41 | success: Color(0xFF1DB83A), 42 | done: Color(0xFF9F9F9F), 43 | fail: Color(0xFFD70D22), 44 | onboardingTitle: Color(0xFF31439B), 45 | btnText1: Color(0xFFFCFCFC)), 46 | suffix: '_dark'); 47 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/theme/theme_light.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'themes.dart'; 4 | 5 | final lightTheme = Themes( 6 | colors: Colours( 7 | appBar: Color(0xFFFFFFFF), 8 | border: Color(0xFFD8D8D8), 9 | bottomBar: Color(0xFFFFFFFF), 10 | modelSheet: Color(0xFFFFFFF), 11 | row: Color(0x52D8D8D8), 12 | banner: Color(0x4DD8D8D8), 13 | card: Color(0xFFFFFFFF), 14 | button3: Color(0xFF21409A), 15 | button2: Color(0xFF000000), 16 | button1: Color(0xFF4D4084), 17 | button4: Color(0xFFF0F0F0), 18 | primary: Color(0xFF000000), 19 | background: Color(0xFFFFFFFF), 20 | selectedItemBackground: Color(0x274D4084), 21 | text1: Color(0x274D4084), 22 | text2: Color(0xFF21409A), 23 | text3: Color(0xFF4D4084), 24 | text4: Color(0xC98D8D8D), 25 | text5: Color(0xFFB00020), 26 | text6: Color(0xFFFFFFFF), 27 | text7: Color(0xFF787878), 28 | text8: Color(0xFF3B3B3B), 29 | text9: Color(0xFF383838), 30 | text10: Color(0xFF515151), 31 | text11: Color(0xFF080808), 32 | text12: Color(0xFF00B203), 33 | text13: Color(0xFF292929), 34 | error: Color(0xFFC52D2D), 35 | gradientStart1: Color(0xFF645AFF), 36 | gradientEnd1: Color(0xFFA573FF), 37 | disabled: Color(0x4D21409A), 38 | dot: Color(0x50FFFFFF), 39 | divider: Color(0xAA979797), 40 | pending: Color(0xFFFFAE42), 41 | success: Color(0xFF1DB83A), 42 | done: Color(0xFF9F9F9F), 43 | fail: Color(0xFFD70D22), 44 | onboardingTitle: Color(0xFF31439B), 45 | btnText1: Color(0xFFFCFCFC)), 46 | suffix: ''); 47 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/theme/theme_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_midtrans/data/local/storage.dart'; 3 | import 'package:flutter_midtrans/helpers/theme/theme_dark.dart'; 4 | import 'package:flutter_midtrans/helpers/theme/theme_light.dart'; 5 | 6 | import 'themes.dart'; 7 | 8 | class ThemeProvider with ChangeNotifier { 9 | ThemeMode themeMode; 10 | 11 | ThemeProvider() { 12 | if (themeMode == null) themeMode = storage.getThemeMode(); 13 | } 14 | 15 | Themes getTheme(BuildContext context) => getBrightness(context) == Brightness.dark ? darkTheme : lightTheme; 16 | 17 | Brightness getBrightness(BuildContext context) { 18 | switch (themeMode) { 19 | case ThemeMode.dark: 20 | return Brightness.dark; 21 | case ThemeMode.light: 22 | return Brightness.light; 23 | default: // system 24 | return MediaQuery.of(context).platformBrightness; 25 | } 26 | } 27 | 28 | setThemeMode(ThemeMode themeMode) { 29 | this.themeMode = themeMode; 30 | storage.setThemeMode(themeMode); 31 | notifyListeners(); 32 | } 33 | } 34 | 35 | class ThemeEnum { 36 | final String name, title, description; 37 | final themeMode, index; 38 | 39 | const ThemeEnum._internal(this.name, this.title, this.themeMode, this.index, {this.description}); 40 | 41 | toString() => name; 42 | 43 | static const Light = const ThemeEnum._internal('Light', 'Light Theme', ThemeMode.light, 0); 44 | static const Dark = const ThemeEnum._internal('Dark', 'Dark Theme', ThemeMode.dark, 1); 45 | static const System = const ThemeEnum._internal('Auto', 'Adapt to System Setting', ThemeMode.system, 2, 46 | description: '''Adapt automatically to the device’s system setting between dark and light themes.'''); 47 | 48 | static getTitleList() => [Light.title, Dark.title, System.title]; 49 | 50 | static getDescriptionList() => [Light.description, Dark.description, System.description]; 51 | 52 | static getTheme(int index) { 53 | if (index == Light.index) 54 | return Light; 55 | else if (index == Dark.index) 56 | return Dark; 57 | else 58 | return System; 59 | } 60 | 61 | static getThemeByThemeMode(String themeMode) { 62 | if (themeMode == Light.themeMode.toString()) 63 | return Light; 64 | else if (themeMode == Dark.themeMode.toString()) 65 | return Dark; 66 | else 67 | return System; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/theme/themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | 4 | const Bold = FontWeight.w700; 5 | const SemiBold = FontWeight.w600; 6 | const Medium = FontWeight.w500; 7 | const Regular = FontWeight.w400; 8 | 9 | class Themes { 10 | final Colours colors; 11 | final TextStyles styles = TextStyles(); 12 | final String suffix; 13 | 14 | Themes({@required this.colors, @required this.suffix}); 15 | 16 | SvgPicture getSvgPicture(String name, {double width, double height, Key key}) => SvgPicture.asset( 17 | 'assets/$name$suffix.svg', 18 | width: width, 19 | height: height, 20 | key: key, 21 | ); 22 | } 23 | 24 | class Colours { 25 | final Color primary, 26 | border, 27 | appBar, 28 | banner, 29 | card, 30 | row, 31 | modelSheet, 32 | bottomBar, 33 | background, 34 | selectedItemBackground, 35 | error, 36 | fail, 37 | pending, 38 | success, 39 | done, 40 | text1, 41 | text2, 42 | text3, 43 | text4, 44 | text5, 45 | text6, 46 | text7, 47 | text8, 48 | text9, 49 | text10, 50 | text11, 51 | text12, 52 | text13, 53 | disabled, 54 | divider, 55 | button1, 56 | button2, 57 | button3, 58 | button4, 59 | btnText1, 60 | gradientStart1, 61 | gradientEnd1, 62 | dot, 63 | onboardingTitle; 64 | 65 | Colours({ 66 | @required this.primary, 67 | @required this.border, 68 | @required this.banner, 69 | @required this.card, 70 | @required this.modelSheet, 71 | @required this.row, 72 | @required this.bottomBar, 73 | @required this.appBar, 74 | @required this.background, 75 | @required this.selectedItemBackground, 76 | @required this.error, 77 | @required this.fail, 78 | @required this.success, 79 | @required this.done, 80 | @required this.pending, 81 | @required this.gradientStart1, 82 | @required this.gradientEnd1, 83 | @required this.text1, 84 | @required this.text2, 85 | @required this.text3, 86 | @required this.text4, 87 | @required this.text5, 88 | @required this.text6, 89 | @required this.text7, 90 | @required this.text8, 91 | @required this.text9, 92 | @required this.text10, 93 | @required this.text11, 94 | @required this.text12, 95 | @required this.text13, 96 | @required this.disabled, 97 | @required this.dot, 98 | @required this.button1, 99 | @required this.button2, 100 | @required this.button3, 101 | @required this.button4, 102 | @required this.divider, 103 | @required this.onboardingTitle, 104 | @required this.btnText1, 105 | }); 106 | } 107 | 108 | class TextStyles { 109 | final TextStyle title1, 110 | title2, 111 | title3, 112 | title4, 113 | title5, 114 | subTitle1, 115 | subTitle2, 116 | subTitle3, 117 | subTitle4, 118 | subTitle5, 119 | subTitle6, 120 | subTitle7, 121 | body1, 122 | body2, 123 | body3, 124 | body4, 125 | body5, 126 | body6, 127 | body7, 128 | button1, 129 | button2, 130 | button3, 131 | button4, 132 | bottomLabel1, 133 | bottomLabel2; 134 | 135 | TextStyles({ 136 | this.title1 = const TextStyle(fontWeight: Bold, fontSize: 34), 137 | this.title2 = const TextStyle(fontWeight: Bold, fontSize: 24), 138 | this.title3 = const TextStyle(fontWeight: Bold, fontSize: 22), 139 | this.title4 = const TextStyle(fontWeight: Bold, fontSize: 20), 140 | this.title5 = const TextStyle(fontWeight: Bold, fontSize: 18), 141 | this.subTitle1 = const TextStyle(fontWeight: Medium, fontSize: 25), 142 | this.subTitle2 = const TextStyle(fontWeight: Medium, fontSize: 20), 143 | this.subTitle3 = const TextStyle(fontWeight: Medium, fontSize: 18), 144 | this.subTitle4 = const TextStyle(fontWeight: Medium, fontSize: 17), 145 | this.subTitle5 = const TextStyle(fontWeight: Medium, fontSize: 16), 146 | this.subTitle6 = const TextStyle(fontWeight: Medium, fontSize: 15), 147 | this.subTitle7 = const TextStyle(fontWeight: Medium, fontSize: 14), 148 | this.body1 = const TextStyle(fontWeight: Regular, fontSize: 17), 149 | this.body2 = const TextStyle(fontWeight: Regular, fontSize: 16), 150 | this.body3 = const TextStyle(fontWeight: Regular, fontSize: 15), 151 | this.body4 = const TextStyle(fontWeight: Regular, fontSize: 14), 152 | this.body5 = const TextStyle(fontWeight: Regular, fontSize: 12), 153 | this.body6 = const TextStyle(fontWeight: Regular, fontSize: 10), 154 | this.body7 = const TextStyle(fontWeight: Regular, fontSize: 9), 155 | this.button1 = const TextStyle(fontWeight: SemiBold, fontSize: 16), 156 | this.button2 = const TextStyle(fontWeight: SemiBold, fontSize: 14), 157 | this.button3 = const TextStyle(fontWeight: SemiBold, fontSize: 11), 158 | this.button4 = const TextStyle(fontWeight: SemiBold, fontSize: 11), 159 | this.bottomLabel1 = const TextStyle(fontWeight: SemiBold, fontSize: 12), 160 | this.bottomLabel2 = const TextStyle(fontWeight: SemiBold, fontSize: 12), 161 | }); 162 | } 163 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/helpers/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:fluttertoast/fluttertoast.dart'; 4 | 5 | class Utils { 6 | static final Utils _singleton = new Utils._internal(); 7 | 8 | factory Utils() { 9 | return _singleton; 10 | } 11 | 12 | Utils._internal(); 13 | 14 | // only use this if reuse image for both dark & light theme 15 | // otherwise using getSvgPicture in Themes class 16 | static getSvgPicture(String name) => SvgPicture.asset('assets/$name.svg'); 17 | 18 | static bool isValidEmail(String email) { 19 | String p = r"^[a-zA-Z0-9._]+@[a-zA-Z0-9]+\.[a-zA-Z]+"; 20 | RegExp regExp = new RegExp(p); 21 | return regExp.hasMatch(email); 22 | } 23 | 24 | bool isEmptyString(String str) { 25 | return str == null || str.isEmpty; 26 | } 27 | 28 | toast(dynamic str) { 29 | Fluttertoast.showToast( 30 | msg: str, 31 | toastLength: Toast.LENGTH_LONG, 32 | gravity: ToastGravity.BOTTOM, 33 | timeInSecForIosWeb: 3, 34 | backgroundColor: Colors.green, 35 | textColor: Colors.white, 36 | fontSize: 15.0); 37 | } 38 | } 39 | 40 | final utils = new Utils(); 41 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_midtrans/config/config.dart'; 2 | import 'package:flutter_midtrans/main_configured.dart'; 3 | 4 | void main() { 5 | Config.setEnvironment(Environment.Local); 6 | mainDelegate(); 7 | } 8 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/main_configured.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:flutter_midtrans/data/local/storage.dart'; 7 | import 'package:flutter_midtrans/helpers/theme/theme_provider.dart'; 8 | import 'package:flutter_midtrans/helpers/utils.dart'; 9 | import 'package:flutter_midtrans/routes.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | class SimpleBlocObserver extends BlocObserver { 13 | @override 14 | void onEvent(Bloc bloc, Object event) { 15 | super.onEvent(bloc, event); 16 | } 17 | 18 | @override 19 | void onTransition(Bloc bloc, Transition transition) { 20 | super.onTransition(bloc, transition); 21 | } 22 | 23 | @override 24 | void onError(Cubit cubit, Object error, StackTrace stacktrace) { 25 | super.onError(cubit, error, stacktrace); 26 | utils.toast(error.toString()); 27 | } 28 | } 29 | 30 | void mainDelegate() async { 31 | WidgetsFlutterBinding.ensureInitialized(); 32 | 33 | runZoned(() async { 34 | Bloc.observer = SimpleBlocObserver(); 35 | await storage.init(); 36 | 37 | runApp(ChangeNotifierProvider( 38 | create: (_) => ThemeProvider(), 39 | child: MaterialApp( 40 | debugShowCheckedModeBanner: false, 41 | home: Routes(), 42 | ), 43 | )); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/repositories/product_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_midtrans/helpers/api_response.dart'; 3 | import 'package:flutter_midtrans/services/api.dart'; 4 | import 'package:flutter_midtrans/services/response.dart'; 5 | 6 | class ProductRepository { 7 | final BaseAPI api; 8 | 9 | ProductRepository({ 10 | @required this.api, 11 | }) : assert(api != null); 12 | 13 | Future loadProducts() async { 14 | var response = await handleResponse(api.loadProducts()); 15 | if (!response.success) return null; 16 | return ListProductResponse.fromMap(response.data); 17 | } 18 | 19 | Future buyProduct({product}) async { 20 | var response = await handleResponse(api.purchase(product)); 21 | if (!response.success) return null; 22 | return BaseResponse(response.data); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/root.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_midtrans/screens/home.dart'; 3 | 4 | class Root extends StatefulWidget { 5 | final String showCase; 6 | const Root({this.showCase, Key key}) : super(key: key); 7 | 8 | @override 9 | _RootState createState() => _RootState(); 10 | } 11 | 12 | class _RootState extends State { 13 | @override 14 | void initState() { 15 | super.initState(); 16 | } 17 | 18 | @override 19 | void dispose() { 20 | super.dispose(); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return HomeScreen(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_midtrans/config/config.dart'; 3 | import 'package:flutter_midtrans/helpers/theme/theme_provider.dart'; 4 | import 'package:flutter_midtrans/helpers/theme/themes.dart'; 5 | import 'package:flutter_midtrans/screen_router.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class Routes extends StatelessWidget { 9 | const Routes({Key key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | var screenRouter = ScreenRouter(); 14 | final Themes theme = Provider.of(context).getTheme(context); 15 | App.theme = theme; 16 | 17 | return GestureDetector( 18 | onTap: () { 19 | WidgetsBinding.instance.focusManager.primaryFocus?.unfocus(); 20 | }, 21 | child: MaterialApp( 22 | debugShowCheckedModeBanner: false, 23 | theme: ThemeData( 24 | platform: TargetPlatform.iOS, 25 | backgroundColor: theme.colors.background, 26 | primaryColor: theme.colors.primary, 27 | accentColor: theme.colors.primary, 28 | buttonColor: theme.colors.primary, 29 | bottomAppBarColor: theme.colors.primary, 30 | fontFamily: 'Georgia', 31 | scaffoldBackgroundColor: theme.colors.background, 32 | bottomSheetTheme: BottomSheetThemeData( 33 | backgroundColor: theme.colors.bottomBar, 34 | elevation: 5, 35 | shape: RoundedRectangleBorder( 36 | borderRadius: BorderRadius.vertical(top: Radius.circular(20)), 37 | ), 38 | ), 39 | appBarTheme: AppBarTheme( 40 | color: theme.colors.appBar, 41 | iconTheme: IconThemeData(color: theme.colors.primary), 42 | elevation: 0, 43 | ), 44 | unselectedWidgetColor: theme.colors.button3, 45 | // for checkbox 46 | dividerTheme: DividerThemeData(thickness: 1, color: theme.colors.divider)), 47 | onGenerateRoute: screenRouter.generateRoute, 48 | onUnknownRoute: screenRouter.unknownRoute, 49 | initialRoute: '/', 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/screen_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_midtrans/blocs/product/product_cubit.dart'; 4 | import 'package:flutter_midtrans/repositories/product_repo.dart'; 5 | import 'package:flutter_midtrans/root.dart'; 6 | import 'package:flutter_midtrans/screens/snap.dart'; 7 | import 'package:flutter_midtrans/services/api.dart'; 8 | import 'package:flutter_midtrans/widgets/common/button_widget.dart'; 9 | 10 | class ScreenRouter { 11 | static const ROOT = '/'; 12 | static const SNAP = '/snap'; 13 | 14 | static const ARG_TRANSACTION_TOKEN = 'ARG_TRANSACTION_TOKEN'; 15 | 16 | BaseAPI api; 17 | var productRepo; 18 | 19 | ScreenRouter() { 20 | api = API(); 21 | productRepo = ProductRepository(api: api); 22 | } 23 | 24 | Route generateRoute(RouteSettings settings) { 25 | var route = buildPageRoute(settings); 26 | Map arguments = settings.arguments; 27 | 28 | switch (settings.name) { 29 | case ROOT: 30 | return route(Root()); 31 | case SNAP: 32 | return route(SnapScreen( 33 | transactionToken: arguments[ARG_TRANSACTION_TOKEN], 34 | )); 35 | default: 36 | return unknownRoute(settings); 37 | } 38 | } 39 | 40 | Function buildPageRoute(RouteSettings settings) { 41 | var blocProviders = [ 42 | BlocProvider(create: (context) => ProductCubit(productRepo)), 43 | ]; 44 | 45 | return (Widget child, {fullScreen = false}) => MaterialPageRoute( 46 | fullscreenDialog: fullScreen, 47 | builder: (context) => MultiBlocProvider( 48 | providers: blocProviders, 49 | child: child, 50 | ), 51 | settings: settings, 52 | ); 53 | } 54 | 55 | Route unknownRoute(RouteSettings settings) { 56 | var unknownRouteText = "No such screen for ${settings.name}"; 57 | 58 | return PageRouteBuilder(pageBuilder: (context, animation, secondaryAnimation) { 59 | return Column( 60 | mainAxisAlignment: MainAxisAlignment.center, 61 | children: [ 62 | Text(unknownRouteText), 63 | Padding(padding: const EdgeInsets.all(10.0)), 64 | ButtonWidget( 65 | title: "Back", 66 | onPressed: () { 67 | Navigator.of(context).pop(); 68 | }, 69 | ), 70 | ], 71 | ); 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/screens/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_midtrans/blocs/product/product_cubit.dart'; 3 | import 'package:flutter_midtrans/blocs/product/product_state.dart'; 4 | import 'package:flutter_midtrans/config/config.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:flutter_midtrans/data/models/product.dart'; 7 | import 'package:flutter_midtrans/screen_router.dart'; 8 | import 'package:flutter_midtrans/widgets/common/button_widget.dart'; 9 | 10 | class HomeScreen extends StatefulWidget { 11 | @override 12 | _HomeScreenState createState() => _HomeScreenState(); 13 | } 14 | 15 | class _HomeScreenState extends State { 16 | ProductCubit _productCubit; 17 | List _products; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | _productCubit = context.read(); 23 | _productCubit.loadProducts(); 24 | } 25 | 26 | @override 27 | void dispose() { 28 | super.dispose(); 29 | } 30 | 31 | _buildTopItem(Product product) { 32 | return Stack( 33 | children: [ 34 | Padding( 35 | padding: const EdgeInsets.only(top: 50), 36 | child: Align( 37 | alignment: Alignment.bottomRight, 38 | child: App.theme.getSvgPicture('top_bg', height: 400), 39 | ), 40 | ), 41 | Column( 42 | children: [ 43 | Container( 44 | padding: const EdgeInsets.only(left: 15, right: 45, top: 30), 45 | child: Image( 46 | image: AssetImage('assets/${product.productImageName}.png'), 47 | ), 48 | ), 49 | Padding( 50 | padding: const EdgeInsets.only(top: 20, left: 90), 51 | child: Align( 52 | alignment: Alignment.centerLeft, 53 | child: Text( 54 | product.productName, 55 | style: App.theme.styles.title2, 56 | ), 57 | ), 58 | ), 59 | Padding( 60 | padding: const EdgeInsets.only(top: 10, left: 90), 61 | child: Align( 62 | alignment: Alignment.centerLeft, 63 | child: Text( 64 | product.productDesc, 65 | style: App.theme.styles.body1, 66 | ), 67 | ), 68 | ), 69 | Padding( 70 | padding: const EdgeInsets.only(top: 20, left: 90, right: 20), 71 | child: ButtonWidget( 72 | onPressed: () => _productCubit.purchase(product), 73 | title: 'BUY NOW', 74 | ), 75 | ) 76 | ], 77 | ), 78 | ], 79 | ); 80 | } 81 | 82 | _buildItem(Product product) { 83 | return Container( 84 | padding: const EdgeInsets.all(20), 85 | height: 100, 86 | child: Row( 87 | children: [ 88 | Expanded( 89 | flex: 2, 90 | child: Align( 91 | alignment: Alignment.center, 92 | child: Image( 93 | image: AssetImage('assets/${product.productImageName}.png'), 94 | ), 95 | ), 96 | ), 97 | Expanded( 98 | flex: 5, 99 | child: Padding( 100 | padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10), 101 | child: Column( 102 | crossAxisAlignment: CrossAxisAlignment.start, 103 | mainAxisAlignment: MainAxisAlignment.center, 104 | children: [ 105 | Text( 106 | product.productName, 107 | textAlign: TextAlign.left, 108 | style: App.theme.styles.body2, 109 | ), 110 | Text( 111 | product.productDesc, 112 | textAlign: TextAlign.left, 113 | style: App.theme.styles.body3, 114 | ), 115 | ], 116 | ), 117 | ), 118 | ), 119 | Expanded( 120 | flex: 3, 121 | child: ButtonWidget( 122 | title: 'Buy Now', 123 | onPressed: () => _productCubit.purchase(product), 124 | textStyle: App.theme.styles.body3.copyWith(color: Colors.white), 125 | ), 126 | ) 127 | ], 128 | ), 129 | ); 130 | } 131 | 132 | @override 133 | Widget build(BuildContext context) { 134 | return Scaffold( 135 | appBar: AppBar( 136 | centerTitle: false, 137 | leading: InkWell( 138 | child: Icon(Icons.sort), 139 | ), 140 | title: Text( 141 | 'WELCOME', 142 | style: TextStyle( 143 | color: App.theme.colors.text9, 144 | ), 145 | ), 146 | ), 147 | body: RefreshIndicator( 148 | child: BlocListener( 149 | listener: (context, state) { 150 | if (state is ProductLoadedSuccess) { 151 | _products = state.products; 152 | } 153 | 154 | if (state is ProductCreateOrderSuccess) { 155 | Navigator.pushNamed(context, ScreenRouter.SNAP, arguments: { 156 | ScreenRouter.ARG_TRANSACTION_TOKEN: state.transactionToken, 157 | }); 158 | } 159 | }, 160 | child: BlocBuilder(builder: (context, state) { 161 | if (state is ProductLoading) { 162 | return Center( 163 | child: CircularProgressIndicator( 164 | valueColor: AlwaysStoppedAnimation(App.theme.colors.primary), 165 | ), 166 | ); 167 | } 168 | return ListView.builder( 169 | itemCount: _products.length, 170 | itemBuilder: (context, index) { 171 | var product = _products[index]; 172 | 173 | if (index == 0) { 174 | return _buildTopItem(product); 175 | } 176 | 177 | return _buildItem(product); 178 | }, 179 | ); 180 | }), 181 | ), 182 | onRefresh: () async => _productCubit.loadProducts(), 183 | ), 184 | ); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/screens/snap.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_midtrans/config/config.dart'; 5 | import 'package:flutter_midtrans/helpers/utils.dart'; 6 | import 'package:flutter_midtrans/screen_router.dart'; 7 | import 'package:flutter_midtrans/widgets/common/button_widget.dart'; 8 | import 'package:modal_progress_hud/modal_progress_hud.dart'; 9 | import 'dart:io' show Platform; 10 | import 'package:webview_flutter/webview_flutter.dart'; 11 | 12 | class SnapScreen extends StatefulWidget { 13 | final String transactionToken; 14 | 15 | SnapScreen({ 16 | this.transactionToken, 17 | }); 18 | 19 | @override 20 | _SnapScreenState createState() => _SnapScreenState(); 21 | } 22 | 23 | class _SnapScreenState extends State { 24 | WebViewController webViewController; 25 | bool _isLoading = false; 26 | 27 | @override 28 | void initState() { 29 | _isLoading = true; 30 | super.initState(); 31 | } 32 | 33 | @override 34 | void didUpdateWidget(SnapScreen oldWidget) { 35 | super.didUpdateWidget(oldWidget); 36 | } 37 | 38 | @override 39 | void dispose() { 40 | super.dispose(); 41 | } 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return Scaffold( 46 | appBar: AppBar( 47 | title: Text( 48 | 'PAYMENT', 49 | style: TextStyle( 50 | color: App.theme.colors.text9, 51 | ), 52 | ), 53 | elevation: 2, 54 | ), 55 | body: ModalProgressHUD( 56 | inAsyncCall: _isLoading, 57 | child: Stack( 58 | children: [ 59 | WebView( 60 | initialUrl: '', 61 | javascriptMode: JavascriptMode.unrestricted, 62 | gestureNavigationEnabled: true, 63 | javascriptChannels: [ 64 | JavascriptChannel( 65 | name: 'Print', 66 | onMessageReceived: (JavascriptMessage receiver) { 67 | print('==========>>>>>>>>>>>>>> BEGIN'); 68 | print(receiver.message); 69 | if (receiver.message != null || receiver.message != 'undefined') { 70 | if (receiver.message == 'close') { 71 | Navigator.pop(context); 72 | } else { 73 | _handleResponse(receiver.message); 74 | } 75 | } 76 | print('==========>>>>>>>>>>>>>> END'); 77 | }, 78 | ), 79 | JavascriptChannel( 80 | name: 'Android', 81 | onMessageReceived: (JavascriptMessage receiver) { 82 | print('==========>>>>>>>>>>>>>> BEGIN'); 83 | print(receiver.message); 84 | if (Platform.isAndroid) { 85 | if (receiver.message != null || receiver.message != 'undefined') { 86 | if (receiver.message == 'close') { 87 | Navigator.pop(context); 88 | } else { 89 | _handleResponse(receiver.message); 90 | } 91 | } 92 | } 93 | print('==========>>>>>>>>>>>>>> END'); 94 | }, 95 | ), 96 | ].toSet(), 97 | onWebViewCreated: (_controller) { 98 | webViewController = _controller; 99 | _loadHtmlFromAssets(); 100 | }, 101 | onPageFinished: (strURL) { 102 | setState(() { 103 | _isLoading = false; 104 | }); 105 | }, 106 | ), 107 | ], 108 | ), 109 | ), 110 | ); 111 | } 112 | 113 | _loadHtmlFromAssets() { 114 | webViewController.loadUrl(Uri.dataFromString(''' 115 | 116 | 117 | 122 | 123 | 124 | 125 | 150 | 151 | ''', mimeType: 'text/html', encoding: Encoding.getByName('utf-8')).toString()); 152 | } 153 | 154 | _handleResponse(message) { 155 | try { 156 | var title, desc; 157 | Midtrans midtrans; 158 | if (Platform.isAndroid) { 159 | switch (message) { 160 | case 'ok': 161 | midtrans = Midtrans(MIDTRANS_PAYMENT_TYPE.bank_transfer, MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_200); 162 | break; 163 | case 'pending': 164 | midtrans = Midtrans(MIDTRANS_PAYMENT_TYPE.bank_transfer, MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_201); 165 | break; 166 | case 'error': 167 | midtrans = Midtrans(MIDTRANS_PAYMENT_TYPE.bank_transfer, MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_202); 168 | break; 169 | } 170 | } else { 171 | midtrans = Midtrans.fromString(message); 172 | } 173 | var result = midtrans.getResult(); 174 | title = result[0]; 175 | desc = result[1]; 176 | if (title.length == 0 && desc.length == 0) 177 | utils.toast('Something went wrong!'); 178 | else 179 | _showConfirmDialog(title, desc); 180 | } catch (e) { 181 | utils.toast(e.toString()); 182 | } 183 | } 184 | 185 | void _showConfirmDialog(title, desc) async { 186 | await showModalBottomSheet( 187 | context: context, 188 | isScrollControlled: true, 189 | shape: RoundedRectangleBorder( 190 | borderRadius: BorderRadius.vertical(top: Radius.circular(20)), 191 | ), 192 | builder: (BuildContext context) { 193 | return Container( 194 | height: 250, 195 | child: Padding( 196 | padding: const EdgeInsets.all(10), 197 | child: Column( 198 | crossAxisAlignment: CrossAxisAlignment.center, 199 | mainAxisAlignment: MainAxisAlignment.center, 200 | children: [ 201 | Text( 202 | title, 203 | style: App.theme.styles.title3, 204 | ), 205 | SizedBox( 206 | height: 25, 207 | ), 208 | Text( 209 | desc, 210 | style: App.theme.styles.body1, 211 | ), 212 | SizedBox( 213 | height: 25, 214 | ), 215 | ButtonWidget( 216 | onPressed: () => Navigator.of(context).popUntil( 217 | (route) { 218 | return route.settings.name == ScreenRouter.ROOT; 219 | }, 220 | ), 221 | title: 'Close') 222 | ], 223 | ), 224 | ), 225 | ); 226 | }, 227 | ); 228 | } 229 | } 230 | 231 | enum MIDTRANS_PAYMENT_TYPE { 232 | credit_card, 233 | bca_klikpay, 234 | bca_klikbca, 235 | bri_epay, 236 | telkomsel_cash, 237 | bank_transfer, 238 | echannel, 239 | indosat_dompetku, 240 | cstore 241 | } 242 | 243 | const Map MidtransPaymentTypeMap = { 244 | 'credit_card': MIDTRANS_PAYMENT_TYPE.credit_card, 245 | 'bca_klikpay': MIDTRANS_PAYMENT_TYPE.bca_klikpay, 246 | 'bca_klikbca': MIDTRANS_PAYMENT_TYPE.bca_klikbca, 247 | 'bri_epay': MIDTRANS_PAYMENT_TYPE.bri_epay, 248 | 'telkomsel_cash': MIDTRANS_PAYMENT_TYPE.telkomsel_cash, 249 | 'bank_transfer': MIDTRANS_PAYMENT_TYPE.bank_transfer, 250 | 'echannel': MIDTRANS_PAYMENT_TYPE.echannel, 251 | 'indosat_dompetku': MIDTRANS_PAYMENT_TYPE.indosat_dompetku, 252 | 'cstore': MIDTRANS_PAYMENT_TYPE.cstore, 253 | }; 254 | 255 | enum MIDTRANS_STATUS_CODE { 256 | MIDTRANS_STATUS_CODE_200, 257 | MIDTRANS_STATUS_CODE_201, 258 | MIDTRANS_STATUS_CODE_202, 259 | MIDTRANS_STATUS_CODE_300, 260 | MIDTRANS_STATUS_CODE_400, 261 | MIDTRANS_STATUS_CODE_401, 262 | MIDTRANS_STATUS_CODE_402, 263 | MIDTRANS_STATUS_CODE_403, 264 | MIDTRANS_STATUS_CODE_404, 265 | MIDTRANS_STATUS_CODE_405, 266 | MIDTRANS_STATUS_CODE_406, 267 | MIDTRANS_STATUS_CODE_407, 268 | MIDTRANS_STATUS_CODE_408, 269 | MIDTRANS_STATUS_CODE_409, 270 | MIDTRANS_STATUS_CODE_410, 271 | MIDTRANS_STATUS_CODE_411, 272 | MIDTRANS_STATUS_CODE_412, 273 | MIDTRANS_STATUS_CODE_413, 274 | MIDTRANS_STATUS_CODE_500, 275 | MIDTRANS_STATUS_CODE_501, 276 | MIDTRANS_STATUS_CODE_502, 277 | MIDTRANS_STATUS_CODE_503, 278 | MIDTRANS_STATUS_CODE_504, 279 | } 280 | 281 | const Map MidtransStatusCodeMap = { 282 | 200: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_200, 283 | 201: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_201, 284 | 202: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_202, 285 | 300: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_300, 286 | 400: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_400, 287 | 401: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_401, 288 | 402: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_402, 289 | 403: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_403, 290 | 404: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_404, 291 | 405: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_405, 292 | 406: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_406, 293 | 407: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_407, 294 | 408: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_408, 295 | 409: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_409, 296 | 410: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_410, 297 | 411: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_411, 298 | 412: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_412, 299 | 413: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_413, 300 | 500: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_500, 301 | 501: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_501, 302 | 502: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_502, 303 | 503: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_503, 304 | 504: MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_504, 305 | }; 306 | 307 | class Midtrans { 308 | MIDTRANS_PAYMENT_TYPE paymentType; 309 | MIDTRANS_STATUS_CODE statusCode; 310 | 311 | Midtrans(MIDTRANS_PAYMENT_TYPE type, MIDTRANS_STATUS_CODE code) { 312 | paymentType = type; 313 | statusCode = code; 314 | } 315 | 316 | Midtrans.fromString(String message) { 317 | String codeStr = RegExp(r'"status_code".*').stringMatch(message); 318 | int code = int.parse(RegExp(r'\d+;$').stringMatch(codeStr).replaceAll(RegExp(r';'), '')); 319 | statusCode = MidtransStatusCodeMap[code]; 320 | 321 | String typeStr = RegExp(r'"payment_type".*').stringMatch(message); 322 | typeStr = RegExp(r'"?\w+"?;').stringMatch(typeStr).replaceAll(RegExp(r'[;"]'), ''); 323 | paymentType = MidtransPaymentTypeMap[typeStr]; 324 | } 325 | 326 | List getResult() { 327 | String title = '', desc = ''; 328 | switch (statusCode) { 329 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_200: 330 | title = 'Purchase successfully!'; 331 | break; 332 | 333 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_201: 334 | title = 'Purchase successfully!'; 335 | 336 | if (paymentType == MIDTRANS_PAYMENT_TYPE.bank_transfer) { 337 | desc = 'You have 24 hours to send money.'; 338 | } 339 | break; 340 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_202: 341 | title = 'Fraud detection!'; 342 | desc = 'Your payment is denied.'; 343 | break; 344 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_300: 345 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_400: 346 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_401: 347 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_402: 348 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_403: 349 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_404: 350 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_405: 351 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_406: 352 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_407: 353 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_408: 354 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_409: 355 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_410: 356 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_411: 357 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_412: 358 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_413: 359 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_500: 360 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_501: 361 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_502: 362 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_503: 363 | case MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_504: 364 | } 365 | return [title, desc]; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/services/api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:flutter_midtrans/config/config.dart'; 4 | import 'package:flutter_midtrans/data/models/product.dart'; 5 | import 'package:flutter_midtrans/helpers/api_response.dart'; 6 | import 'package:flutter_midtrans/helpers/error_codes.dart'; 7 | import 'package:flutter_midtrans/helpers/logger/tokoin_log.dart'; 8 | 9 | abstract class BaseAPI { 10 | Dio _dio; 11 | 12 | _logging() { 13 | return InterceptorsWrapper(onRequest: (RequestOptions options) async { 14 | log.i("DIO - REQUEST: [${options.method}] ${options.path}} \n" 15 | "body:${options?.data} \n" 16 | "query:${options.queryParameters}"); 17 | 18 | return options; 19 | }, onResponse: (Response response) async { 20 | log.i("DIO - RESPONSE: [${response.request.method}] ${response.request.path} " 21 | "${response?.statusCode} [OK]\n" 22 | "body:${response.request?.data}\n" 23 | "response:${response?.data}"); 24 | return response; 25 | }, onError: (DioError e) async { 26 | log.e("DIO - ERROR: ${e.request.path}\ndata: ${e.request?.data}\nresponse:${e.response?.data}"); 27 | if (e.response?.statusCode == HttpStatus.unauthorized) { 28 | notifyErrorCallbacks(APIUnauthorizedException('unauthorized')); 29 | } else { 30 | notifyErrorCallbacks(e); 31 | } 32 | return e; 33 | }); 34 | } 35 | 36 | void notifyErrorCallbacks(Exception error) { 37 | if (error is DioError) { 38 | if (error.response?.statusCode == HttpStatus.badRequest || 39 | error.response?.statusCode == HttpStatus.internalServerError) { 40 | var data = error.response?.data; 41 | int errorCode = -1; 42 | try { 43 | errorCode = data['code']; 44 | } catch (e) { 45 | log.i(e.toString()); 46 | } 47 | if (ErrorCodes.errorCodes.containsKey(errorCode)) throw APIErrorWithCode(errorCode); 48 | throw APIErrorWithCode(-1); 49 | } 50 | } 51 | } 52 | 53 | Future loadProducts(); 54 | Future purchase(Product product); 55 | } 56 | 57 | class API extends BaseAPI { 58 | static final API _singleton = new API._internal(); 59 | 60 | factory API() { 61 | return _singleton; 62 | } 63 | 64 | API._internal() { 65 | _dio = Dio(); 66 | _dio.options.baseUrl = Config.baseURL; 67 | _dio.options.connectTimeout = 10000; //10s 68 | _dio.options.receiveTimeout = 8000; //8s 69 | _dio.options.responseType = ResponseType.json; 70 | 71 | _dio.interceptors.add(_logging()); 72 | } 73 | 74 | Future loadProducts() async { 75 | return await _dio.get("/product"); 76 | } 77 | 78 | Future purchase(Product product) async { 79 | return await _dio.post("/product/purchase", data: product.toMap()); 80 | } 81 | } 82 | 83 | final api = new API(); 84 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/services/response.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_midtrans/data/models/product.dart'; 2 | 3 | class BaseResponse { 4 | dynamic data; 5 | String message; 6 | BaseResponse(Map map) { 7 | if (map != null) { 8 | message = map['message'] != null ? map['message'] as String : null; 9 | data = map['data']; 10 | } 11 | } 12 | } 13 | 14 | class ListProductResponse extends BaseResponse { 15 | List _products; 16 | 17 | List get products { 18 | return _products ?? (data as List).map((product) => Product.fromMap(product)).toList(); 19 | } 20 | 21 | ListProductResponse.fromMap(Map map) : super(map); 22 | } 23 | -------------------------------------------------------------------------------- /flutter_midtrans/lib/widgets/common/button_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_midtrans/config/config.dart'; 3 | import 'package:flutter_midtrans/helpers/theme/themes.dart'; 4 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 5 | 6 | const DEFAULT_HEIGHT = 40.0; 7 | const DEFAULT_BORDER_WIDTH = 1.0; 8 | const DEFAULT_MARGIN = const EdgeInsets.symmetric(horizontal: 0); 9 | const DEFAULT_PADDING = 15.0; 10 | 11 | enum ButtonType { blue_filled, red_filled, blue_border, white_border, transparent, transparent_danger, small_link } 12 | 13 | class ButtonWidget extends StatefulWidget { 14 | final Function onPressed; 15 | final String title; 16 | final ButtonType buttonType; 17 | final TextStyle textStyle; 18 | final EdgeInsetsGeometry margin; 19 | final bool loading; 20 | final bool enabled; 21 | final bool upperCase; 22 | final double height; 23 | final BorderRadius borderRadius; 24 | final double borderWidth; 25 | final bool fullWidth; 26 | final double padding; 27 | final Alignment alignment; 28 | 29 | const ButtonWidget( 30 | {Key key, 31 | @required this.onPressed, 32 | @required this.title, 33 | this.fullWidth = true, 34 | this.loading = false, 35 | this.enabled = true, 36 | this.buttonType = ButtonType.blue_filled, 37 | this.upperCase = true, 38 | this.height = DEFAULT_HEIGHT, 39 | this.borderRadius = const BorderRadius.all(Radius.circular(8)), 40 | this.textStyle, 41 | this.alignment = Alignment.center, 42 | this.margin = DEFAULT_MARGIN, 43 | this.padding, 44 | this.borderWidth = DEFAULT_BORDER_WIDTH}) 45 | : super(key: key); 46 | 47 | @override 48 | _ButtonWidgetState createState() => _ButtonWidgetState(); 49 | } 50 | 51 | class _ButtonWidgetState extends State { 52 | @override 53 | Widget build(BuildContext context) { 54 | if (widget.fullWidth) return button(); 55 | 56 | return Row( 57 | children: [ 58 | Expanded(flex: 1, child: Container()), 59 | button(), 60 | Expanded(flex: 1, child: Container()), 61 | ], 62 | ); 63 | } 64 | 65 | Widget button() { 66 | Themes theme = App.theme; 67 | Color borderColor, backgroundColor, textColor; 68 | EdgeInsets padding = EdgeInsets.symmetric(horizontal: widget.padding ?? DEFAULT_PADDING); 69 | 70 | switch (widget.buttonType) { 71 | case ButtonType.blue_filled: 72 | borderColor = theme.colors.primary; 73 | backgroundColor = theme.colors.primary; 74 | textColor = Colors.white; 75 | break; 76 | case ButtonType.blue_border: 77 | borderColor = theme.colors.primary; 78 | backgroundColor = Colors.transparent; 79 | textColor = theme.colors.primary; 80 | break; 81 | case ButtonType.white_border: 82 | borderColor = Colors.white; 83 | backgroundColor = Colors.transparent; 84 | textColor = Colors.white; 85 | break; 86 | case ButtonType.red_filled: 87 | borderColor = theme.colors.error; 88 | backgroundColor = theme.colors.error; 89 | textColor = Colors.white; 90 | break; 91 | case ButtonType.transparent: 92 | borderColor = Colors.transparent; 93 | backgroundColor = Colors.transparent; 94 | textColor = theme.colors.primary; 95 | break; 96 | case ButtonType.transparent_danger: 97 | borderColor = Colors.transparent; 98 | backgroundColor = Colors.transparent; 99 | textColor = theme.colors.error; 100 | break; 101 | case ButtonType.small_link: 102 | borderColor = Colors.transparent; 103 | backgroundColor = Colors.transparent; 104 | textColor = theme.colors.primary; 105 | padding = widget.padding == null ? EdgeInsets.all(0) : padding; 106 | break; 107 | } 108 | 109 | if (!canPress()) { 110 | if (widget.buttonType == ButtonType.transparent) { 111 | textColor = textColor.withOpacity(0.5); 112 | } else { 113 | textColor = theme.colors.text6.withOpacity(0.9); 114 | backgroundColor = theme.colors.disabled; 115 | borderColor = borderColor.withOpacity(0); 116 | } 117 | } 118 | 119 | TextStyle textStyle = widget.textStyle ?? theme.styles.button1.copyWith(color: textColor); 120 | 121 | return Container( 122 | height: widget.height, 123 | margin: widget.margin, 124 | decoration: BoxDecoration( 125 | border: Border.all(color: borderColor, width: widget.borderWidth), 126 | borderRadius: widget.borderRadius, 127 | color: backgroundColor), 128 | child: FlatButton( 129 | padding: padding, 130 | onPressed: canPress() ? widget.onPressed : null, 131 | child: Align( 132 | alignment: widget.alignment, 133 | child: widget.loading 134 | ? SpinKitDoubleBounce(color: theme.colors.primary, size: 25) 135 | : Text( 136 | widget.title, 137 | style: textStyle, 138 | ), 139 | ), 140 | ), 141 | ); 142 | } 143 | 144 | canPress() { 145 | return widget.enabled && !widget.loading && widget.onPressed != null; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /flutter_midtrans/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_midtrans 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.7.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^1.0.0 31 | dio: ^3.0.10 32 | flutter_svg: ^0.19.1 33 | fluttertoast: ^7.1.4 34 | flutter_bloc: ^6.1.1 35 | equatable: ^1.2.5 36 | shared_preferences: ^0.5.12+4 37 | flutter_spinkit: ^4.1.2+1 38 | webview_flutter: ^1.0.7 39 | modal_progress_hud: ^0.1.3 40 | logger: 41 | git: 42 | url: https://github.com/trongdth/logger.git 43 | ref: full_print 44 | 45 | dev_dependencies: 46 | flutter_test: 47 | sdk: flutter 48 | 49 | # For information on the generic Dart part of this file, see the 50 | # following page: https://dart.dev/tools/pub/pubspec 51 | 52 | # The following section is specific to Flutter. 53 | flutter: 54 | 55 | # The following line ensures that the Material Icons font is 56 | # included with your application, so that you can use the icons in 57 | # the material Icons class. 58 | uses-material-design: true 59 | 60 | # To add assets to your application, add an assets section, like this: 61 | assets: 62 | - assets/ 63 | 64 | # An image asset can refer to one or more resolution-specific "variants", see 65 | # https://flutter.dev/assets-and-images/#resolution-aware. 66 | 67 | # For details regarding adding assets from package dependencies, see 68 | # https://flutter.dev/assets-and-images/#from-packages 69 | 70 | # To add custom fonts to your application, add a fonts section here, 71 | # in this "flutter" section. Each entry in this list should have a 72 | # "family" key with the font family name, and a "fonts" key with a 73 | # list giving the asset and other descriptors for the font. For 74 | # example: 75 | # fonts: 76 | # - family: Schyler 77 | # fonts: 78 | # - asset: fonts/Schyler-Regular.ttf 79 | # - asset: fonts/Schyler-Italic.ttf 80 | # style: italic 81 | # - family: Trajan Pro 82 | # fonts: 83 | # - asset: fonts/TrajanPro.ttf 84 | # - asset: fonts/TrajanPro_Bold.ttf 85 | # weight: 700 86 | # 87 | # For details regarding fonts from package dependencies, 88 | # see https://flutter.dev/custom-fonts/#from-packages 89 | -------------------------------------------------------------------------------- /flutter_midtrans/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_midtrans/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /screenshots/screen_shot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/screenshots/screen_shot_1.png -------------------------------------------------------------------------------- /screenshots/screen_shot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/screenshots/screen_shot_2.png -------------------------------------------------------------------------------- /screenshots/screen_shot_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trongdth/midtrans/781fe4c0f57007f47290bcdf068b3896d4adc4b6/screenshots/screen_shot_3.png --------------------------------------------------------------------------------