Commit 4bc7b304 by tdgiang

push code

parents
{
"directory": "public/lib"
}
{
"adjoining-classes": false,
"box-model": false,
"box-sizing": false,
"floats": false,
"font-sizes": false,
"important": false,
"known-properties": false,
"overqualified-elements": false,
"qualified-headings": false,
"regex-selectors": false,
"unique-headings": false,
"universal-selector": false,
"unqualified-attributes": false
}
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
.env.test
*.md
log/*
!log/.gitignore
.DS_Store
.vscode
.idea
# EditorConfig is awesome: http://EditorConfig.org
# Howto with your editor:
# Sublime: https://github.com/sindresorhus/editorconfig-sublime
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[**]
end_of_line = lf
insert_final_newline = true
# Standard at: https://github.com/felixge/node-style-guide
[**.js, **.json]
trim_trailing_whitespace = true
indent_style = tab
quote_type = single
curly_bracket_next_line = false
spaces_around_operators = true
space_after_control_statements = true
space_after_anonymous_functions = false
spaces_in_brackets = false
# No Standard. Please document a standard if different from .js
[**.yml, **.html, **.css]
trim_trailing_whitespace = true
indent_style = tab
# No standard. Please document a standard if different from .js
[**.md]
indent_style = tab
# Standard at:
[Makefile]
indent_style = tab
\ No newline at end of file
# Copy to .env and fill in real values. Never commit the real .env file.
SESSION_SECRET=
ENCRYPT_PASSWORD=
ENCRYPT_KEY=
# OmiPay
OMIPAY_ACCOUNT=
OMIPAY_PASSWORD=
OMIPAY_MERCHANT_ID=
OMIPAY_PASSCODE=
OMIPAY_EMAIL_ACCOUNT=
# Epay (Megapay)
EPAY_MERCHANT_ID=
EPAY_ENCODE_KEY=
EPAY_ENCRYPT_KEY=
EPAY_DECRYPT_KEY=
# PVcomBank
PVCOMBANK_TOKEN=
# RSA PEM keys: single line, real newlines replaced with literal \n, wrapped in double quotes.
PVCOMBANK_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
PVCOMBANK_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
PVCOMBANK_BANK_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
# FPT Pay
FPTPAY_MID=
FPTPAY_TID=
FPTPAY_SECRET_KEY=
# OneFin
ONEFIN_PRODUCT_CODE=
ONEFIN_PUBLIC_KEY=
ONEFIN_PRIVATE_KEY=
# Alepay
ALEPAY_TOKEN_KEY=
ALEPAY_ENCRYPT_KEY=
ALEPAY_CHECKSUM_KEY=
# Appota
APPOTA_API_KEY=
APPOTA_SECRET_KEY=
.DS_Store
.env
.nodemonignore
.sass-cache/
npm-debug.log
node_modules/
public/lib
app/tests/coverage/
.bower-*/
.idea/
*.sublime-workspace
*.sublime-project
dist/
\ No newline at end of file
{
"node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
"browser": true, // Standard browser globals e.g. `window`, `document`.
"esnext": true, // Allow ES.next specific features such as `const` and `let`.
"bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
"camelcase": false, // Permit only camelcase for `var` and `object indexes`.
"curly": false, // Require {} for every new block or scope.
"eqeqeq": true, // Require triple equals i.e. `===`.
"immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef": false, // Prohibit variable use before definition.
"newcap": true, // Require capitalization of all constructor functions e.g. `new F()`.
"noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"quotmark": "single", // Define quotes to string values.
"regexp": true, // Prohibit `.` and `[^...]` in regular expressions.
"undef": true, // Require all non-global variables be declared before they are used.
"unused": false, // Warn unused variables.
"strict": true, // Require `use strict` pragma in every file.
"trailing": true, // Prohibit trailing whitespaces.
"smarttabs": false, // Suppresses warnings about mixed tabs and spaces
"sub":true, // turn off better written in dot notation
"globals": { // Globals variables.
"jasmine": true,
"angular": true,
"AppConf": true
},
"predef": [ // Extra globals.
"define",
"require",
"exports",
"module",
"describe",
"before",
"beforeEach",
"after",
"afterEach",
"it",
"inject",
"expect"
],
"globals": {
"angular": true,
"$": true,
"_": true
},
"indent": 4, // Specify indentation spacing
"devel": true, // Allow development statements e.g. `console.log();`.
"noempty": true, // Prohibit use of empty blocks.
"globalstrict": false
}
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project overview
"PaymentGate" (Vietnamese: *Cổng thanh toán*) — a payment gateway aggregator built on a legacy MEAN-stack-style Express server. It exposes payment-initiation, return, cancel, and IPN (webhook) endpoints for multiple Vietnamese payment providers on behalf of a downstream merchant backend (referred to internally as "DCV Finance" / CBOT). It does not process payments itself — it builds signed requests to each provider, and relays creation/status results to an internal admin API (`config.api_host`).
Integrated providers (each with its own credentials/signing scheme in `config/env/all.js`): **OmiPay**, **Epay** (Megapay), **PVcomBank**, **FPT Pay**, **OneFin**, **Alepay**, **Appota**.
## Commands
```bash
npm install # also triggers `bower install` via postinstall
npm start # nodemon server.js in development mode
grunt # == grunt default: cssmin + nodemon:dev + file watch (dev loop)
grunt production # same pipeline, NODE_ENV=production
grunt lint # jshint + csslint
```
There is no working automated test suite. `npm test` runs `grunt test`, but no `test` task is registered in `gruntfile.js` (only `default`, `debug`, `production`, `lint` exist) — it will fail. `karma.conf.js` also points at a `test/`/`src/` layout that doesn't exist in this repo. Don't assume test infrastructure is present; if asked to add tests, a harness needs to be set up first.
## Architecture
**Boot sequence:** `server.js``config/init.js` (resolves `NODE_ENV` from argv or env, defaults to `production`) → `config/config.js` (merges `config/env/all.js` as base with `config/env/<NODE_ENV>.js` as override, via `lodash.extend`) → `config/express.js` (builds the Express app).
**Config layering:** `config/env/all.js` is the *primary* config file — it holds nearly everything, including live third-party credentials (API keys, HMAC secrets, RSA private keys inlined as strings) for every payment provider. `config/env/development.js` and `production.js` only override a handful of fields (`port`, `api_host`, `cache_max_age`, `subdomain_offset`). Note this file (and `app/controllers/dcvf_privatekey.txt`, `dcvf_publickey.txt`, `pvcom_publickey.txt`) contain committed secrets — treat them as sensitive when editing, and don't echo their contents into logs, commits, or unrelated output.
**App wiring (`config/express.js`):** globs and `require()`s every file under `app/models/**` and `app/routes/**` (order matters: models before routes). Sets up Redis-backed sessions (`connect-redis`), Swig as the view engine (`*.server.view.html` templates under `app/views`), Helmet, CORS, and a `checkSubDomain` middleware that parses `req.subDomain` (host/domain/store_code) from the request host — used for multi-tenant subdomain routing. `config/redis.js` exports a standalone redis client but is currently unused (`connect-redis`'s own `RedisStore` is instantiated directly in `express.js`).
**Routing (`app/routes/core.server.routes.js`):** all routes are thin declarations pointing into one controller. Routes are grouped by provider path prefix (`/epay/*`, `/pvcom/*`, `/fptpay/*`, `/onefin/*`, `/appota/*`, `/alepay/*`); OmiPay is the original/oldest integration and keeps unprefixed legacy routes (`/`, `/create-payment-url*`, `/omipay/checkout`, `/omipay-return`, etc.) alongside newer `/omipay/*`-prefixed ones.
**Controller (`app/controllers/core.server.controller.js`, ~2300 lines):** a single monolithic file implements every provider. Each provider follows the same four-endpoint shape:
- `create*PaymentUrl` / `create*PaymentLink` — reads order params from `req.body`, builds the provider-specific signed payload (RSA-SHA256 signing for PVcomBank via `node-rsa`/`crypto`, HMAC-SHA256 for Appota, provider-specific hashing elsewhere), logs the transaction via `ApiRequest.post('<provider>log/create', ...)` against the internal admin API, then calls out to the provider's API via `ApiRequest.postOtherUrl`.
- `*Return` — renders `frontend/paymentSuccess` (or a provider-specific page) after the user is redirected back.
- `*IPN` — the provider's server-to-server webhook; verifies the signature/hash, then calls `ApiRequest.post('<provider>log/update', ...)` to update transaction status on the internal admin API.
- `*Cancel` — renders `frontend/paymentCancel`.
Shared helpers live at the bottom of the same file: `renderPage()` (thin `res.render` wrapper), `encrypt`/`decrypt` (AES-256-CBC, used e.g. by OmiPay checkout deep links), `generateJwt`/`isValidSignatureAppota` (Appota-specific auth). When adding a new provider, follow this same create/return/ipn/cancel pattern rather than introducing a new structure.
**`app/libs/ApiRequest.js`:** the only HTTP client used to talk to (a) the internal admin/backend API at `config.api_host` (`post`, `get`, `postAuth`, `getAuth`, `putAuth`, `delAuth` — all prefix the configured `api_host`), and (b) arbitrary external URLs, i.e. the payment providers themselves (`postOtherUrl`, `getOtherUrl`, `postOtherUrlWithHeader`). Built on the legacy `request` package with a Node-style `(err, body)` callback, not promises.
**Views:** Swig templates (`*.server.view.html`) under `app/views`, with provider-specific frontend checkout pages under `app/views/frontend/` (e.g. `epay-index`, `pvcom-index`, `omipayCheckout`). `app/views/404.server.view.html` / `500.server.view.html` are the global error pages wired up in `config/express.js`.
**Models:** a single Mongoose model, `app/models/Order.js` — mostly legacy/unrelated fields from a shared upstream schema; only `total_price`/`status` are meaningfully used by this service. Session state and most order bookkeeping actually live on the external admin API via `ApiRequest`, not in this repo's own MongoDB.
**Dockerfile is stale**: it's an unmodified MEAN.js boilerplate (bower/grunt install, `EXPOSE 8084`) that doesn't match the app's real port (`8091` dev / `3003` prod, from `config/env/*.js`) or its actual Node dependency versions — don't rely on it as the current deployment recipe without checking with the team.
FROM node:20-alpine
WORKDIR /app
# Install dependencies first so this layer is cached unless package*.json changes.
# --ignore-scripts skips the "postinstall: bower install" step - bower only
# manages legacy frontend assets under public/lib, not needed by the payment API.
COPY package.json package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts --legacy-peer-deps
COPY . .
RUN mkdir -p log && chown -R node:node /app
USER node
ENV NODE_ENV=production
EXPOSE 3003
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD wget -qO- http://127.0.0.1:3003/ >/dev/null || exit 1
CMD ["node", "server.js"]
## License
(The MIT License)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This source diff could not be displayed because it is too large. You can view the blob instead.
POSS, build in NodeJs, Express, Cassandra, Angular
Utils:
setSimpleColumns(column, hasEditColumn);
setColumnAttr(attrs)
http://ui-grid.info/docs/#/api/ui.grid.class:GridOptions.columnDef
attrs:
- enableColumnMenu
- enableSorting
- width
- cellTemplate: '<div class="ui-grid-cell-edit"></div>'
- name
- displayName
- sort: {direction: 'asc'}
- cellFilter
- cellClass
- visible
- cellTooltip
- enableHiding
- headerCellClass
setSubGridOptions(subGridOptionName, columns, dataColumnName, translatePrefix)
Directive: ui-select
https://github.com/brianreavis/selectize.js/blob/master/docs/usage.md
Example: http://plnkr.co/edit/X2YYPX?p=preview
Thuộc tính:
- options: list model
- config: config object
- ng-model
- placeholder
Config Object:
- create: cho phép tự động thêm vào danh sách nếu item không tồn tại
- valueField: tên trường cho giá trị của 1 item
- labelField: tên trường cho tên hiển thị của 1 item
- searchField: mảng field muốn search
- render: đối tượng render: {item, option, option_create, optgroup_header, optgroup}
- onChange(value, $item): sự kiện change dropdown
- load(query, callback): sự kiện khi type, dùng lazy load
Coding Conversation:, optgroup
- $scope.model : đối tượng chứa data trên form
- $scope.list : đối tượng chứa danh sách sử dụng trong dropdown
- $scope.grid : đối tượng chứa grid options
- $scope.filter : đối tượng chứa data filter
Fix Node module Multiparty, line 196 with comment 'comment this code for pass error Request aborted when using part stream'
Copy collection to another database:
db.<collection_name>.find().forEach(function(d){ db.getSiblingDB('<new_database>')['<collection_name>'].insert(d); });
// script ví dụ update db hàng loạt
db.customers.find().forEach(
function (e) {
// update document, using its own properties
if (e.debits && e.debits.length>0) {
var isChange = false;
for (var i=0;i<e.debits.length;i++) {
if (!e.debits[i].date_occur) {
if (e.debits[i].order_type=='payment_voucher' || e.debits[i].order_type=='receipt_voucher') {
var myCashflow = db.cashflows.findOne( { _id: e.debits[i].order_id } );
if (myCashflow) {
e.debits[i].date_occur = myCashflow.dateCreated;
}
} else {
var myOrder = db.orders.findOne( { _id: e.debits[i].order_id } );
if (myOrder) {
e.debits[i].date_occur = myOrder.dateCreated;
}
}
isChange=true;
}
delete e.debits[i].user_created;
delete e.debits[i].dateCreated;
}
}
// save the if document changed
if (isChange) {
db.customers.save(e);
}
}
)
db.orders.find().forEach(
function (e) {
var total = 0;
if (e.products && e.products.length>0) {
for (var i=0;i<e.products.length;i++) {
if (e.products[i].unit_in !== e.products[i].unit_out) {
total += e.products[i].price_begin * (e.products[i].quantity / e.products[i].unit_inout_rate);
} else {
total += e.products[i].price_begin * e.products[i].quantity;
}
}
}
e.total_price_begin = total;
db.orders.save(e);
}
)
\ No newline at end of file
'use strict';
/**
* Get unique error field name
*/
var getUniqueErrorMessage = function(err) {
var output;
try {
var fieldName = err.err.substring(err.err.lastIndexOf('.$') + 2, err.err.lastIndexOf('_1'));
output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists';
} catch (ex) {
output = 'Unique field already exists';
}
return output;
};
/**
* Get the error message from error object
*/
exports.getErrorMessage = function(err) {
var message = '';
if (err.code) {
switch (err.code) {
case 11000:
case 11001:
message = getUniqueErrorMessage(err);
break;
default:
message = 'Something went wrong';
}
} else {
for (var errName in err.errors) {
if (err.errors[errName].message) message = err.errors[errName].message;
}
}
return message;
};
\ No newline at end of file
'use strict';
/**
* ApiRequest: thư viện hỗ trợ tạo request lên server api
*/
var config = require('../../config/config'),
request = require('request');
exports.post = function (apiName, param, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var options = {
method: 'POST',
uri: config.api_host + apiName,
json: param
};
if (param && param.token) {
options['auth'] = {
'bearer': param.token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
exports.get = function (apiName, param, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var url = config.api_host + apiName;
for (var k in param) {
url += k + '=' + param[k] + '&';
}
var options = {
method: 'GET',
uri: url,
json: true
};
if (param && param.token) {
options['auth'] = {
'bearer': param.token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
exports.getAuth = function (apiName, param, token, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var url = config.api_host + apiName;
for (var k in param) {
url += k + '=' + param[k] + '&';
}
var options = {
method: 'GET',
uri: url,
json: true
};
if (token) {
options['auth'] = {
'bearer': token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
exports.postAuth = function (apiName, param, token, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var options = {
method: 'POST',
uri: config.api_host + apiName,
json: param
};
if (token) {
options['auth'] = {
'bearer': token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
exports.putAuth = function (apiName, param, token, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var options = {
method: 'PUT',
uri: config.api_host + apiName,
json: param
};
if (token) {
options['auth'] = {
'bearer': token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
exports.delAuth = function (apiName, param, token, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var url = config.api_host + apiName;
for (var k in param) {
url += k + '=' + param[k] + '&';
}
var options = {
method: 'DELETE',
uri: url,
json: true
};
if (token) {
options['auth'] = {
'bearer': token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
exports.postOtherUrl = function (apiName, param, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var options = {
method: 'POST',
uri: apiName,
json: param
};
if (param && param.token) {
options['auth'] = {
'bearer': param.token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
exports.getOtherUrl = function (apiName, param, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var url = apiName;
for (var k in param) {
url += k + '=' + param[k] + '&';
}
var options = {
method: 'GET',
uri: url,
json: true
};
if (param && param.token) {
options['auth'] = {
'bearer': param.token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
exports.postOtherUrlWithHeader = function (apiName, param, headers, callback) {
if (typeof (param) === 'function') {
callback = param;
param = {};
}
var options = {
method: 'POST',
uri: apiName,
headers: headers,
json: param
};
if (param && param.token) {
options['auth'] = {
'bearer': param.token
};
}
// request ato API Host
request(options, function (err, httpResponse, body) {
if (err) {
callback(err, body);
} else if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
callback(err, body);
} else {
callback(body);
}
});
};
'use strict';
var async = require('async');
var fs = require('fs');
var _ = require('lodash');
var config = require('../../config/config');
exports.concatJsFileContents = concatJsFileContents;
exports.concatJsonFileContents = concatJsonFileContents;
exports.concatFileContents = concatFileContents;
exports.readMultiFile = readMultiFile;
// --------------------- Utils Function ---------------------
function concatJsFileContents(buffers, cb) {
cb(null, Buffer.concat(buffers));
}
function concatJsonFileContents(buffers, cb) {
var json = {};
_.forEach(buffers, function(item) {
try {
json = _.merge(json, JSON.parse(item.toString()));
} catch (ex) {}
});
cb(null, json);
}
function concatFileContents(buffers, cb) {
var r = '';
_.forEach(buffers, function(item) {
r += item.toString() + '\n';
});
cb(null, r);
}
function readMultiFile(files, cb) {
async.map(
files,
readFile,
cb
);
}
// Cache lại nội dung file, dùng cho lần sau yêu cầu
var cacheFile = {};
function readFile(file, cb) {
if (config.mode !== 'dev' && cacheFile[file]) {
cb(null, cacheFile[file]);
} else {
fs.readFile(file, function(err, data) {
if (!err) cacheFile[file] = data;
cb(err, data);
})
}
}
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var OrderSchema = new Schema({
dmserver_id: String,
user_new: String,
date_new: String,
trang_thai_tk: Number,
kd_insert: Number,
lan_gia_han: Number,
ten_dvcs: String,
dia_chi: String,
server_name: String,
hh_ctv_dl: String,
pt_nguoigt: String,
da_thu_tien_hd: String,
da_thu_ho_so: String,
ngay_hop_dong: String,
id: String,
index: String,
dmkh_id: String,
loai_kh: String,
ms_thue: String,
ten_kh: String,
diachi_lh: String,
co_quan_thue: String,
tinh_thanh: String,
nguoi_dai_dien: String,
chuc_vu: String,
sdt: String,
email: String,
website: String,
stk: String,
ngan_hang: String,
nguoi_lien_he: String,
sdt_nguoi_lien_he: String,
email_nguoi_lien_he: String,
so_luong_hd: String,
choose: Number,
ma_dvcs: String,
ghi_chu: String,
ngay_kich_hoat: String,
trang_thai_db: String,
end_user: String,
is_trial: Boolean,
date_expried: String,
order_name: String,
ma_giao_dich: String,
total_price: {
type: Number,
default: 0
},
status: {
type: Number,
default: 0
}
});
module.exports = mongoose.model('Order', OrderSchema);
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"/": {
"title": "Hiệp hội Mắc Ca Việt Nam"
},
"/san-pham": {
"title": "Sản phẩm - Hiệp hội Mắc Ca Việt Nam"
},
"/dung-thu": {
"title": "Đăng ký dùng thử - Hiệp hội Mắc Ca Việt Nam"
},
"/bao-gia": {
"title": "Báo giá - Hiệp hội Mắc Ca Việt Nam"
},
"/tin-tuc": {
"title": "Tin tức - Hiệp hội Mắc Ca Việt Nam"
},
"/khach-hang": {
"title": "Khách hàng - Hiệp hội Mắc Ca Việt Nam"
},
"/ho-tro": {
"title": "Hỗ trợ - Hiệp hội Mắc Ca Việt Nam"
},
"/dang-nhap": {
"title": "Đăng nhập - Hiệp hội Mắc Ca Việt Nam"
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
'use strict';
module.exports = function (app) {
// Root routing
var core = require('../../app/controllers/core.server.controller');
// OMIPAY
app.route('/').get(core.index);
app.route('/with-token').get(core.indexWithToken);
app.route('/create-payment-url').post(core.createPaymentUrl);
app.route('/create-payment-url-with-token').post(core.createPaymentUrlWithToken);
app.route('/create-payment-url-golive').post(core.createPaymentUrlGolive);
app.route('/create-payment-url-golive-with-id').post(core.createPaymentUrlGoliveWithId);
app.route('/create-payment-url-golive-out-token').post(core.createPaymentUrlGoliveOutToken);
app.route('/omipay/checkout').get(core.omipayCheckout);
app.route('/omipay-return').get(core.omiPayReturn);
app.route('/omipay-cancel').get(core.omipayCancel);
app.route('/omipay-ipn').get(core.omipayIPN);
// EPAY
app.route('/epay/payment').get(core.epayIndex);
app.route('/epay/payment-with-token').get(core.indexWithToken);
app.route('/epay/create-payment-url').post(core.epayCreatePaymentUrl);
app.route('/epay/return').get(core.epayReturn);
app.route('/epay/ipn').post(core.epayIPN);
// FPT PAY
app.route('/fptpay/create-payment-url-golive').post(core.fptPayCreatePaymentLink);
app.route('/fptpay/return').get(core.fptPayReturn);
app.route('/fptpay/cancel').get(core.fptPayCancel);
app.route('/fptpay/ipn').post(core.fptPayIPN);
// PVCOMBANK
app.route('/pvcom/create-payment-url').post(core.pvcomCreatePaymentUrl);
app.route('/pvcom/return').get(core.pvcomReturn);
app.route('/pvcom/cancel').get(core.pvcomCancel);
app.route('/pvcom/ipn').post(core.pvcomIPN);
// ONE FIN
app.route('/onefin/create-payment-url-golive').post(core.onefinCreatePaymentLink);
app.route('/onefin/return').get(core.onefinReturn);
app.route('/onefin/cancel').get(core.onefinCancel);
app.route('/onefin/ipn').post(core.onefinIPN);
// APPOTA
app.route('/appota/create-payment-url-golive').post(core.appotaCreatePaymentLink);
app.route('/appota/return').get(core.appotaReturn);
app.route('/appota/cancel').get(core.appotaCancel);
app.route('/appota/ipn').post(core.appotaIPN);
// Alepay
app.route('/alepay/create-payment-url-golive').post(core.alePayCreatePaymentLink);
app.route('/alepay/return').get(core.alePayReturn);
app.route('/alepay/cancel').get(core.alePayCancel);
app.route('/alepay/ipn').post(core.alePayIPN);
};
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>HDV - Lỗi 404-Nội dung không tìm thấy</title>
<link href="/img/favicon1.ico" rel="icon" type="image/ico">
<link href="assets/global/css/bootstrap.min.css" rel="stylesheet">
<link href="assets/global/css/font-awesome.min.css" rel="stylesheet">
<link href="assets/global/css/404.webstore.css" rel="stylesheet">
</head>
<body>
<!--start-wrap-->
<div class="wrap">
<!--start-header-->
<div class="header">
<div class="logo">
<h1><a href="#">Ohh</a></h1>
</div>
</div>
<!---End-header-->
<!--start-content-->
<div class="content">
<img src="https://i.imgur.com/whAnpUe.png" title="Lỗi" />
<p id="urlNotFound" class="hide"><span><label>O</label>hh.....</span>Trang web bạn yêu cầu không tồn tại</p>
</div>
<!--End-Cotent-->
</div>
<!--End-wrap-->
<script type="text/javascript">
var domains = ['vn', 'org', 'com'];
var pathname = window.location.pathname;
var hostUrl = window.location.host;
var hosts = hostUrl.split('.');
var $register = document.getElementById('register');
var $storeNotFound = document.getElementById('storeNotFound');
var $urlNotFound = document.getElementById('urlNotFound');
var $home = document.getElementById('home');
$register.setAttribute('href', window.location.protocol + '//' + hostUrl + '/bang-gia');
if (pathname === '/cua_hang_khong_ton_tai') {
// neu co subdoimain thi remove no roi set lai link cua home, register
// local -> length ==2;
if (hosts.length === 2 || (hosts.length > 2 && domains.indexOf(hosts[hosts.length - 1]) > -1)) {
hosts.splice(0, 1);
hostUrl = hosts.join('.');
$home.setAttribute('href', window.location.protocol + '//' + hostUrl);
$register.setAttribute('href', window.location.protocol + '//' + hostUrl + '/bang-gia');
}
$storeNotFound.classList.remove("hide");
$register.classList.remove("hide");
} else {
$urlNotFound.classList.remove("hide");
if (hosts[0] === 'id') {
hosts.splice(0, 1);
$home.setAttribute('href', window.location.protocol + '//' + hosts.join('.'));
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>HDV - Lỗi 500-Server tạm dừng</title>
<link href="/img/favicon1.ico" rel="icon" type="image/ico">
<link href="/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/app.min.css" rel="stylesheet">
<link href="/assets/global/plugins/font-awesome/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
<div class="container w-xxl w-auto-xs">
<div class="text-center m-b-lg">
<h1 class="text-shadow text-white">500</h1>
<h3>Server tạm dừng.</h3>
</div>
</div>
</body>
<script type="text/javascript">
document.getElementById('back').setAttribute('href', '/' + window.location.hash);
</script>
</html>
{% extends 'layout-epay.server.view.html' %} {% block cssFile %} {% endblock %} {% block content %}
<style>
.m-t-10 {
margin-top: 10px;
}
.full-width {
width: 100% !important;
}
</style>
<div class="row full-width">
<div class="col-sm-9 offset-2">
<input type="hidden" id="agentCode" name="agentCode" value="{{agentCode}}" />
<input type="hidden" id="createdBy" name="createdBy" value="{{createdBy}}" />
<input type="hidden" id="types" name="types" value="{{types}}" />
<input type="hidden" id="branchId" name="branchId" value="{{branchId}}" />
<input type="hidden" id="types" name="types" value="{{types}}" />
<form id="megapayForm" name="megapayForm" method="POST">
<input type="hidden" name="invoiceNo" value="OrdNo" />
<input type="hidden" name="description" value="Thanh toán đơn hàng" />
<input type="hidden" name="goodsNm" value="Sản phẩm giáo dục" />
<input type="hidden" name="currency" value="VND">
<input type="hidden" name="buyerPhone" value="">
<input type="hidden" name="buyerAddr" value="">
<input type="hidden" name="buyerCity" value="">
<input type="hidden" name="buyerState" value="" />
<input type="hidden" name="buyerPostCd" value="" />
<input type="hidden" name="buyerCountry" value="vn" />
<input type="hidden" name="fee" value="" />
<!-- Delivery Info -->
<input type="hidden" name="receiverFirstNm" value="">
<input type="hidden" name="receiverLastNm" value="">
<input type="hidden" name="receiverPhone" value="">
<input type="hidden" name="receiverAddr" value="">
<input type="hidden" name="receiverCity" value="">
<input type="hidden" name="receiverState" value="" />
<input type="hidden" name="receiverPostCd" value="" />
<input type="hidden" name="receiverCountry" value="VN" />
<!------------------------------- Main Value ------------------------------>
<input type="hidden" name="callBackUrl" value="" />
<!-- Notify URL -->
<input type="hidden" name="notiUrl" value="" />
<!-- Merchant ID -->
<input type="hidden" name="merId" value='' />
<!-- Encode Key -->
<!-- Loai thanh toan -->
<input type="hidden" name="payOption" id="payOption" value="{{payOpt}}" id="payOption" />
<!-- <input type="hidden" name="payOption" id="payOption" value="PAY_CREATE_TOKEN" id="payOption" /> -->
<!-- <input type="hidden" name="payOption" id="payOption" value="PAY_WITH_TOKEN" id="payOption" /> -->
<input type="hidden" name="reqDomain" value="" />
<input type="hidden" name="mer_temp01" value="" />
<input type="hidden" name="mer_temp02" value="" />
<input type="hidden" name="userLanguage" value="VN" />
<input type="hidden" name="merchantToken" value="" />
<input type="hidden" name="payToken" id="payToken" value="" />
<input type="hidden" name="timeStamp" value="" />
<input type="hidden" name="merTrxId" />
<input type="hidden" name="windowType" value="1" />
<input type='hidden' name='windowColor' value='#0B3B39' />
<input type="hidden" name="vaCondition" value="03" />
<input type="hidden" name="subappid" id="subappid" value="" />
<input type="hidden" name="userId" id="userId" value="{{userId}}" />
<div class="row full-width">
<div class="col-md-12" style="display: none;">
<label class="col-form-label">Phương thức thanh toán</label>
<br>
<div class="row full-width">
<div class="col-md-3">
<input type="radio" name="payType" id="payType" value="IC" checked/>
Visa/Master/JBC
</div>
</div>
</div>
<div class="col-md-12">
<label class="col-form-label">Số tiền thanh toán</label>
<br>
<input type="text" name="amount" id="amount" value="{{amount}}" />
</div>
<div class="col-md-12">
<label class="col-form-label">Tên</label>
<br>
<input type="text" name="buyerLastNm" id="buyerLastNm" value="{{buyerLastNm}}" />
</div>
<div class="col-md-12">
<label class="col-form-label">Họ</label>
<br>
<input type="text" name="buyerFirstNm" id="buyerFirstNm" value="{{buyerFirstNm}}" />
</div>
<div class="col-md-12">
<label class="col-form-label">Email</label>
<br>
<input type="text" name="buyerEmail" id="buyerEmail" value="nguyenvanc@gmail.com" />
</div>
</div>
</form>
<div class="col-md-12" style="margin-top: 15px;">
<input class="btn btn-primary" type="submit" value="Thanh toán" onclick="validateForm();">
</div>
</div>
</div>
<!-- <div class="col-12" id="omipay-iframe"></div> -->
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="{{epayDomain}}/pg_was/js/payment/layer/paymentClient.js"></script>
<script type="text/javascript" src="/js/epay/payment.js">
</script>
{% endblock %}
{% block jsFile %}
{% endblock %}
\ No newline at end of file
{% extends 'layout.server.view.html' %} {% block cssFile %} {% endblock %} {% block content %}
<style>
.m-t-10 {
margin-top: 10px;
}
.full-width {
width: 100% !important;
}
</style>
<form action="payment-form" method="post">
<h3 class="col-md-8 offset-2 m-t-10"><b>1. Thông tin đơn hàng</b></h3>
<div class="col-md-8 offset-2">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Sản phẩm</th>
<th scope="col">Số tiền</th>
<th scope="col">Đơn vị</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Dụng cụ giáo dục</td>
<td>
<input id="price" name="price" type="number" value="10000" required />
</td>
<td>vnd</td>
</tr>
</tbody>
</table>
</div>
<h3 class="col-md-8 offset-2 m-t-10"><b>2. Thông tin khách hàng</b></h3>
<div class="col-md-8 offset-2">
<label class="col-form-label">Họ tên</label>
<br>
<input id="full_name" name="full_name" type="text" class="form-control" placeholder="Nhập tên" required />
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Số điện thoại</label>
<br>
<input id="phone_number" name="phone-number" type="text" class="form-control" placeholder="Nhập..." required />
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Địa chỉ</label>
<br>
<textarea id="address" name="address" rows="3" type="text" class="form-control"
placeholder="Nhập..."></textarea>
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Nội dung thanh toán</label>
<br>
<textarea id="trans_info" name="trans-info" rows="3" type="text" class="form-control"
placeholder="Nhập..."></textarea>
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Thời gian hết hạn</label>
<br>
<input id="expire_date" name="expire-date" type="date" class="form-control" placeholder="Nhập..." />
</div>
<h3 class="col-md-8 offset-2 m-t-10"><b>3. Phương thức thanh toán</b></h3>
<div class="col-md-8 offset-2">
<label class="col-form-label">Phương thức thanh toán</label>
<br>
<select id="payment_method_id" name="payment_method_id" class="form-control" required>
<option value="">-- Chọn phương thức thanh toán --</option>
<option value="1095">Visa</option>
<option value="1098">MasterCard</option>
<option value="1101">JCB</option>
</select>
</div>
<!-- <div class="col-md-8 offset-2">
<label class="col-form-label">Số thẻ</label>
<br>
<input id="card_no" type="number" class="form-control" placeholder="Nhập số thẻ" required />
</div> -->
<input type="hidden" name="inpage" value="0">
<div class="col-md-8 offset-2 m-t-10 text-center">
<button type="button" id="omi-btn-checkout" class="btn btn-primary" style="margin-bottom: 30px;">Thanh toán
ngay</button>
</div>
</form>
<input type="hidden" id="valueOmi" value="{{valueOmi}}">
<!-- <div class="col-12" id="omipay-iframe"></div> -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<!-- <script type="text/javascript" src="https://upload-sandbox.omipay.vn/public/js/inpage.checkout.js"></script> -->
<script>
$(document).ready(() => {
setTimeout(function () {
var valueOmi = $("#valueOmi").val();
if (valueOmi) {
window.location.href = valueOmi;
}
}, 100);
$("#omi-btn-checkout").click(() => {
let price = $('#price').val();
let full_name = $('#full_name').val();
let phone_number = $('#phone_number').val();
let address = $('#address').val();
let trans_info = $('#trans_info').val();
let expire_date = $('#expire_date').val();
var payment_method_element = document.getElementById("payment_method_id");
let payment_method_id = payment_method_element.value;
if (price == null || price == undefined) {
alert("Bạn chưa nhập đơn giá");
return;
}
if (full_name == null || full_name == undefined) {
alert("Bạn chưa nhập Họ tên");
return;
}
if (phone_number == null || phone_number == undefined) {
alert("Bạn chưa nhập Số điện thoại");
return;
}
if (payment_method_id == null || payment_method_id == undefined) {
alert("Bạn chưa nhập Phương thức thanh toán");
return;
}
var requestData = {
price: price,
full_name: full_name,
phone_number: phone_number,
address: address,
trans_info: trans_info,
expire_date: expire_date,
payment_method_id: payment_method_id
};
$.post("/create-payment-url", requestData, function (x) {
if (x.code === '00') {
window.location.href = x.data;
return false;
} else {
alert(x.Message);
}
});
})
});
</script>
{% endblock %}
{% block jsFile %}
{% endblock %}
\ No newline at end of file
{% extends 'layout.server.view.html' %} {% block cssFile %} {% endblock %} {% block content %}
<style>
.m-t-10 {
margin-top: 10px;
}
.full-width {
width: 100% !important;
}
</style>
<form action="payment-form" method="post">
<h3 class="col-md-8 offset-2 m-t-10"><b>1. Thông tin đơn hàng</b></h3>
<div class="col-md-8 offset-2">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Sản phẩm</th>
<th scope="col">Số tiền</th>
<th scope="col">Đơn vị</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Dụng cụ giáo dục</td>
<td>
<input id="price" name="price" type="number" value="10000" required />
</td>
<td>vnd</td>
</tr>
</tbody>
</table>
</div>
<h3 class="col-md-8 offset-2 m-t-10"><b>2. Thông tin khách hàng</b></h3>
<div class="col-md-8 offset-2">
<label class="col-form-label">Họ tên</label>
<br>
<input id="full_name" name="full_name" type="text" class="form-control" placeholder="Nhập tên" required />
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Số điện thoại</label>
<br>
<input id="phone_number" name="phone-number" type="text" class="form-control" placeholder="Nhập..." required />
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Địa chỉ</label>
<br>
<textarea id="address" name="address" rows="3" type="text" class="form-control" placeholder="Nhập..."></textarea>
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Nội dung thanh toán</label>
<br>
<textarea id="trans_info" name="trans-info" rows="3" type="text" class="form-control" placeholder="Nhập..."></textarea>
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Thời gian hết hạn</label>
<br>
<input id="expire_date" name="expire-date" type="date" class="form-control" placeholder="Nhập..." />
</div>
<h3 class="col-md-8 offset-2 m-t-10"><b>3. Phương thức thanh toán</b></h3>
<div class="col-md-8 offset-2">
<label class="col-form-label">Phương thức thanh toán</label>
<br>
<select id="payment_method_id" name="payment_method_id" class="form-control" required>
<option value="">-- Chọn phương thức thanh toán --</option>
<option value="1095">Visa</option>
<option value="1098">MasterCard</option>
<option value="1101">JCB</option>
</select>
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Số thẻ</label>
<br>
<input id="card_no" type="number" class="form-control" placeholder="Nhập số thẻ" required />
</div>
<input type="hidden" name="inpage" value="0">
<div class="col-md-8 offset-2 m-t-10 text-center">
<button type="button" id="omi-btn-checkout" class="btn btn-primary" style="margin-bottom: 30px;">Thanh toán
ngay</button>
</div>
</form>
<!-- <div class="col-12" id="omipay-iframe"></div> -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<!-- <script type="text/javascript" src="https://upload-sandbox.omipay.vn/public/js/inpage.checkout.js"></script> -->
<script>
$(document).ready(() => {
$("#omi-btn-checkout").click(() => {
let price = $('#price').val();
let full_name = $('#full_name').val();
let phone_number = $('#phone_number').val();
let address = $('#address').val();
let trans_info = $('#trans_info').val();
let expire_date = $('#expire_date').val();
let card_no = $('#card_no').val();
var payment_method_element = document.getElementById("payment_method_id");
let payment_method_id = payment_method_element.value;
if (price == null || price == undefined) {
alert("Bạn chưa nhập đơn giá");
return;
}
if (full_name == null || full_name == undefined) {
alert("Bạn chưa nhập Họ tên");
return;
}
if (phone_number == null || phone_number == undefined) {
alert("Bạn chưa nhập Số điện thoại");
return;
}
if (card_no == null || card_no == undefined) {
alert("Bạn chưa nhập Số thẻ");
return;
}
if (payment_method_id == null || payment_method_id == undefined) {
alert("Bạn chưa nhập Phương thức thanh toán");
return;
}
var requestData = {
price: price,
full_name: full_name,
phone_number: phone_number,
address: address,
trans_info: trans_info,
expire_date: expire_date,
card_no: card_no,
payment_method_id: payment_method_id
};
$.post("/create-payment-url-with-token", requestData, function (x) {
if (x.code === '00') {
window.location.href = x.data;
return false;
} else {
alert(x.Message);
}
});
})
});
</script>
{% endblock %}
{% block jsFile %}
{% endblock %}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{title}}</title>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="content-language" content="vi" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="revisit-after" content="1 days">
<meta name="author" content="LVT">
<meta name="copyright " content="Copyright (c) 2020 LVT">
<meta name="keywords" content="{{keywords}}">
<meta name="description" content="{{description}}">
<link rel="alternate" href="" hreflang="vi-vn" />
<link href="/img/favico1.png" rel="icon" type="image/ico">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!-- <link href="https://upload-sandbox.omipay.vn/public/css/icheckout.min.css" rel="stylesheet" type="text/css"> -->
<link rel="stylesheet" href="{{epayDomain}}/pg_was/css/payment/layer/paymentClient.css">
<script src="{{epayDomain}}/pg_was/js/payment/layer/paymentClient.js"></script>
{% block cssFile %}{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{title}}</title>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="content-language" content="vi" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="revisit-after" content="1 days">
<meta name="author" content="LVT">
<meta name="copyright " content="Copyright (c) 2020 LVT">
<meta name="keywords" content="{{keywords}}">
<meta name="description" content="{{description}}">
<link rel="alternate" href="" hreflang="vi-vn" />
<link href="/img/favico1.png" rel="icon" type="image/ico">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!-- <link href="https://upload-sandbox.omipay.vn/public/css/icheckout.min.css" rel="stylesheet" type="text/css"> -->
{% block cssFile %}{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
</body>
</html>
\ No newline at end of file
{% extends 'layout.server.view.html' %} {% block cssFile %} {% endblock %} {% block content %}
<h4 class="text-center">Bạn phải cho phép bật popup để tiến hành thanh toán!</h4>
<input class="hidden" id="omipayCheckout" value="{{valueOmi}}" width="1000" height="1000" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script>
$(document).ready(() => {
var value = $("#omipayCheckout").val();
setTimeout(() => {
if (value) {
window.location.href = value;
}
}, 300);
});
</script>
{% endblock %}
{% block jsFile %}
{% endblock %}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>Hủy thanh toán</title>
<link href="/img/favicon1.ico" rel="icon" type="image/ico">
<link href="/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/app.min.css" rel="stylesheet">
<link href="/assets/global/plugins/font-awesome/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
<div class="container w-xxl w-auto-xs">
<div class="text-center m-b-lg">
<h1 class="text-shadow text-white">Hủy thanh toán thành công!</h1>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>Thanh toán</title>
<link href="/img/favicon1.ico" rel="icon" type="image/ico">
</head>
<body>
{% if data && data.error_text %}
<h1 class="text-shadow text-white">Thanh toán thất bại!</h1>
<h4 class="text-shadow text-white">Lý do: {{data.error_text}}</h4>
{% else %}
<div>
<div style="margin-left: auto;
margin-right: auto;
display: block; display: flex;align-items: center;justify-content: center;">
<img style="margin: auto;" width="60" height="60"
src="https://apigw.dcvfinance.com/download/dcvf/icSucess_175411070520241.png" alt="alternatetext">
</div>
<h3 style="font-size: 30px;color: green;text-align: center;">Thanh toán đơn hàng thành công</h3>
<hr>
<div style="margin-left: 10%;">
{% if data && data.createdBy %}
<div style="display: flex;min-height: 35px;max-height: 40px;">
<p style="width: 230px;font-size: 18px;font-weight: 700;margin-right: 10px;">Người xử lý</p>
<p style="font-size: 18px;">{{data.createdBy}}</p>
</div>
{% endif %}
{% if data && data.merTrxId %}
<div style="display: flex;min-height: 35px;max-height: 40px;">
<p style="width: 230px;font-size: 18px;font-weight: 700;margin-right: 10px;">Mã giao dịch</p>
<p style="font-size: 18px;">{{data.merTrxId}}</p>
</div>
{% endif %}
<div style="display: flex;min-height: 35px;max-height: 40px;">
<p style="width: 230px;font-size: 18px;font-weight: 700;margin-right: 10px;">Trạng thái</p>
<p style="font-size: 18px;color: green;">Thành công</p>
</div>
{% if data && data.amount %}
<div style="display: flex;min-height: 35px;max-height: 40px;">
<p style="width: 230px;font-size: 18px;font-weight: 700;margin-right: 10px;">Tổng tiền</p>
<p style="font-size: 18px;">{{data.amount}} vnđ</p>
</div>
{% endif %}
{% if data && data.transactionInfo %}
<div style="display: flex;min-height: 35px;max-height: 40px;">
<p style="width: 230px;font-size: 18px;font-weight: 700;margin-right: 10px;">Nội dung</p>
<p style="font-size: 18px;">{{data.transactionInfo}}</p>
</div>
{% endif %}
{% if data && data.payType %}
<div style="display: flex;min-height: 35px;max-height: 40px;">
<p style="width: 230px;font-size: 18px;font-weight: 700;margin-right: 10px;">Phương thức thanh
toán</p>
<p style="font-size: 18px;">{{data.payType}}</p>
</div>
{% endif %}
{% if data && data.cardNo %}
<div style="display: flex;min-height: 35px;max-height: 40px;">
<p style="width: 230px;font-size: 18px;font-weight: 700;margin-right: 10px;">Số thẻ</p>
<p style="font-size: 18px;">{{data.cardNo}}</p>
</div>
{% endif %}
{% if data && data.transactionDate %}
<div style="display: flex;min-height: 35px;max-height: 40px;">
<p style="width: 230px;font-size: 18px;font-weight: 700;margin-right: 10px;">Thời gian thanh
toán</p>
<p style="font-size: 18px;">{{data.transactionDate}}</p>
</div>
{% endif %}
</div>
</div>
{% endif %}
</body>
</html>
\ No newline at end of file
{% extends 'layout.server.view.html' %} {% block cssFile %} {% endblock %} {% block content %}
<style>
.m-t-10 {
margin-top: 10px;
}
.full-width {
width: 100% !important;
}
</style>
<form action="payment-form" method="post">
<h3 class="col-md-8 offset-2 m-t-10"><b>1. Thông tin đơn hàng</b></h3>
<div class="col-md-8 offset-2">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Sản phẩm</th>
<th scope="col">Số tiền</th>
<th scope="col">Đơn vị</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Dụng cụ giáo dục</td>
<td>
<input id="price" name="price" type="number" value="10000" required />
</td>
<td>vnd</td>
</tr>
</tbody>
</table>
</div>
<h3 class="col-md-8 offset-2 m-t-10"><b>2. Thông tin khách hàng</b></h3>
<div class="col-md-8 offset-2">
<label class="col-form-label">Họ tên</label>
<br>
<input id="full_name" name="full_name" type="text" class="form-control" placeholder="Nhập tên" required />
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Email</label>
<br>
<input id="email" name="email" type="text" class="form-control" placeholder="Nhập..." required />
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Số điện thoại</label>
<br>
<input id="phone_number" name="phone-number" type="text" class="form-control" placeholder="Nhập..." required />
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Địa chỉ</label>
<br>
<textarea id="address" name="address" rows="3" type="text" class="form-control" placeholder="Nhập..."></textarea>
</div>
<div class="col-md-8 offset-2">
<label class="col-form-label">Nội dung thanh toán</label>
<br>
<textarea id="trans_info" name="trans-info" rows="3" type="text" class="form-control" placeholder="Nhập..."></textarea>
</div>
<input type="hidden" name="inpage" value="0">
<div class="col-md-8 offset-2 m-t-10 text-center">
<button type="button" id="omi-btn-checkout" class="btn btn-primary" style="margin-bottom: 30px;">Thanh toán
ngay</button>
</div>
</form>
<!-- <div class="col-12" id="omipay-iframe"></div> -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<!-- <script type="text/javascript" src="https://upload-sandbox.omipay.vn/public/js/inpage.checkout.js"></script> -->
<script>
$(document).ready(() => {
$("#omi-btn-checkout").click(() => {
let price = $('#price').val();
let full_name = $('#full_name').val();
let phone_number = $('#phone_number').val();
let address = $('#address').val();
let trans_info = $('#trans_info').val();
if (price == null || price == undefined) {
alert("Bạn chưa nhập đơn giá");
return;
}
if (full_name == null || full_name == undefined) {
alert("Bạn chưa nhập Họ tên");
return;
}
if (phone_number == null || phone_number == undefined) {
alert("Bạn chưa nhập Số điện thoại");
return;
}
var requestData = {
price: price,
customer_name: full_name,
customer_email: email,
customer_phone: phone_number,
address: address,
trans_info: trans_info
};
$.post("/pvcom/create-payment-url", requestData, function (x) {
if (x.code === '00') {
window.location.href = x.data;
return false;
} else {
alert(x.Message);
}
});
})
});
</script>
{% endblock %}
{% block jsFile %}
{% endblock %}
\ No newline at end of file
{
"name": "POSS",
"dependencies": {
"angular": "~1.5.9",
"angular-sanitize": "~1.5.9"
},
"devDependencies": {}
}
'use strict';
/**
* Module dependencies.
*/
var _ = require('lodash');
var glob = require('glob');
/**
* Load app configurations
*/
module.exports = _.extend(
require('./env/all'),
require('./env/' + (process.env.NODE_ENV)) || {}
);
/**
* Get files by glob patterns
*/
module.exports.getGlobbedFiles = function(globPatterns, removeRoot) {
// For context switching
var _this = this;
// URL paths regex
var urlRegex = new RegExp('^(?:[a-z]+:)?\/\/', 'i');
// The output array
var output = [];
// If glob pattern is array so we use each pattern in a recursive way, otherwise we use glob
if (_.isArray(globPatterns)) {
globPatterns.forEach(function(globPattern) {
output = _.union(output, _this.getGlobbedFiles(globPattern, removeRoot));
});
} else if (_.isString(globPatterns)) {
if (urlRegex.test(globPatterns)) {
output.push(globPatterns);
} else {
var files = glob.sync(globPatterns);
if (removeRoot) {
files = files.map(function(file) {
return file.replace(removeRoot, '');
});
}
output = _.union(output, files);
}
}
return output;
};
"use strict";
// PEM keys are stored in .env as a single line with literal "\n" escapes
// (real newlines don't survive well in .env files), so restore them here.
function pemFromEnv(value) {
return value ? value.replace(/\\n/g, "\n") : value;
}
module.exports = {
app: {
title: "Siêu thị",
description: "PaymentGate - Cổng thanh toán",
keywords: "PaymentGate",
},
domain: "localhost:8091",
root: require("path").normalize(__dirname + "/../.."),
version: "0.1.7",
version_patch: "1",
port: 8091,
templateEngine: "swig",
sessionSecret: process.env.SESSION_SECRET,
sessionCollection: "frontend_sessions",
api_host: "https://apigw.dcvfinance.com/api/",
// api_host: " https://localhost:44366/api/",
subdomain_offset: 1,
maxUploadFileSize: 204800, // 200Kb
remember_me_max_age: 604800000, // 7 days
session_max_age: 86400000, // 1 days
offline_appcache: false,
analytic: false,
log_file:
require("path").normalize(__dirname + "/../..") + "/log/frontend-error.log",
encrypt: {
algorithm: "aes-256-ctr",
password: process.env.ENCRYPT_PASSWORD,
},
omipay: {
omp_account: process.env.OMIPAY_ACCOUNT,
omp_password: process.env.OMIPAY_PASSWORD,
omp_merchant_id: process.env.OMIPAY_MERCHANT_ID,
omp_passcode: process.env.OMIPAY_PASSCODE,
return_url: "https://pg.cbotvietnam.com/omipay-return",
cancel_url: "https://pg.cbotvietnam.com/omipay-cancel",
notify_url: "https://pg.cbotvietnam.com/omipay-ipn",
email_account: process.env.OMIPAY_EMAIL_ACCOUNT,
payment_link: "https://checkout.omipay.vn/checkout.php",
},
epay: {
merchant_id: process.env.EPAY_MERCHANT_ID,
// Production by default; set EPAY_DOMAIN=https://sandbox.megapay.vn to test against sandbox.
domain: process.env.EPAY_DOMAIN || "https://pg.megapay.vn",
encode_key: process.env.EPAY_ENCODE_KEY,
encrypt_key: process.env.EPAY_ENCRYPT_KEY,
decrypt_key: process.env.EPAY_DECRYPT_KEY,
// Overridable so a tunnel URL (ngrok/localtunnel) can be used for sandbox testing.
req_domain: process.env.EPAY_REQ_DOMAIN || "https://pg.dcvfinance.vn",
return_url:
process.env.EPAY_RETURN_URL || "https://pg.dcvfinance.vn/epay/return",
notify_url:
process.env.EPAY_NOTIFY_URL || "https://pg.dcvfinance.vn/epay/ipn",
},
pvcombank: {
token: process.env.PVCOMBANK_TOKEN,
base_url: "https://api.ecomit.vn/",
return_url: "https://pg.dcvfinance.vn/pvcom/return",
cancel_url: "https://pg.dcvfinance.vn/pvcom/cancel",
notify_url: "https://pg.dcvfinance.vn/pvcom/ipn",
// Our RSA key pair, used to sign outgoing payment requests (private_key)
// and previously handed to PVcomBank out-of-band (public_key).
private_key: pemFromEnv(process.env.PVCOMBANK_PRIVATE_KEY),
public_key: pemFromEnv(process.env.PVCOMBANK_PUBLIC_KEY),
// PVcomBank's own RSA public key, used to verify IPN callback signatures.
bank_public_key: pemFromEnv(process.env.PVCOMBANK_BANK_PUBLIC_KEY),
},
fptpay: {
base_url: "https://portal.foxpay.vn/payment/initiate",
version: "3.0",
mid: process.env.FPTPAY_MID,
tid: process.env.FPTPAY_TID,
secret_key: process.env.FPTPAY_SECRET_KEY,
return_url: "https://pg.cbotvietnam.com/fptpay/return",
cancel_url: "https://pg.cbotvietnam.com/fptpay/cancel",
ipn_url: "https://pg.cbotvietnam.com/fptpay/ipn",
},
onefin: {
base_url: "https://pgw.onefin.vn/public/v3/mweb/generatePayment",
return_url: "https://pg.cbotvietnam.com/onefin/return",
cancel_url: "https://pg.cbotvietnam.com/onefin/cancel",
ipn_url: "https://pg.cbotvietnam.com/onefin/ipn",
productCode: process.env.ONEFIN_PRODUCT_CODE,
"publickey-OF": process.env.ONEFIN_PUBLIC_KEY,
"privatekey-MC": process.env.ONEFIN_PRIVATE_KEY,
},
alepay: {
base_url: "https://alepay-v3.nganluong.vn/api/v3/checkout",
token_key: process.env.ALEPAY_TOKEN_KEY,
encrypt_key: process.env.ALEPAY_ENCRYPT_KEY,
checksum_key: process.env.ALEPAY_CHECKSUM_KEY,
return_url: "https://pg.cbotvietnam.com/alepay/return",
cancel_url: "https://pg.cbotvietnam.com/alepay/cancel",
},
appota: {
base_url: "https://gateway.appotapay.com/api/v2/orders/payment",
partner_code: "CBOT",
api_key: process.env.APPOTA_API_KEY,
secret_key: process.env.APPOTA_SECRET_KEY,
return_url: "https://pg.cbotvietnam.com/appota/return",
cancel_url: "https://pg.cbotvietnam.com/appota/cancel",
ipn_url: "https://pg.cbotvietnam.com/appota/ipn",
},
encrypt_key: process.env.ENCRYPT_KEY,
};
'use strict';
module.exports = {
mode: 'dev',
cache_max_age: 0, // bỏ qua cache ở chế độ development
socket_host: 'http://localhost:8081',
api_host: 'http://123.25.24.162:9988/admin/', // url api
app: {
title: 'Cổng thanh toán'
}
};
'use strict';
module.exports = {
mode: 'pro',
port: 3003,
subdomain_offset: 2,
cache_max_age: 31536000000
};
"use strict";
module.exports = {
mode: "test",
port: 8092,
// The real internal ledger API (config.api_host in all.js / development.js)
// is only reachable from DCV's internal network. For local/sandbox testing
// of the Epay (MegaPay) flow, point at a local stub implementing the same
// paymentgateledger endpoints (see scripts or scratchpad mock server).
api_host: process.env.TEST_API_HOST || "http://localhost:9999/",
cache_max_age: 0,
};
'use strict';
/**
* Module dependencies.
*/
var fs = require('fs'),
http = require('http'),
https = require('https'),
express = require('express'),
morgan = require('morgan'),
bodyParser = require('body-parser'),
session = require('express-session'),
compress = require('compression'),
methodOverride = require('method-override'),
cookieParser = require('cookie-parser'),
helmet = require('helmet'),
favicon = require('serve-favicon'),
RedisStore = require('connect-redis')(session),
config = require('./config'),
consolidate = require('consolidate'),
path = require('path'),
_ = require('lodash'),
log = require('./log');
var swigCache = {};
var swig = require('swig');
var numeral = require('numeral');
var cors = require('cors');
module.exports = function() {
// Initialize express app
var app = express();
app.use(cors());
app.use(favicon('./public/img/favico1.png'));
// Globbing model files
config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
require(path.resolve(modelPath));
});
// Setting application local variables
app.locals.title = config.app.title;
app.locals.version = config.version;
app.locals.description = config.app.description;
app.locals.keywords = config.app.keywords;
app.locals.epayDomain = config.epay.domain;
// app.set('subdomain offset', config.subdomain_offset);
// Showing stack errors
app.set('showStackError', true);
// Set swig as the template engine
app.engine('server.view.html', consolidate[config.templateEngine]);
// Set views path and view engine
app.set('view engine', 'server.view.html');
app.set('views', ['./app/views']);
// Environment dependent middleware
var developMode;
if (process.env.NODE_ENV === 'production') {
app.locals.cache = 'memory';
developMode = false;
// Enable logger (morgan)
app.use(morgan('short'));
} else if (process.env.NODE_ENV !== 'test') {
developMode = true;
// Enable logger (morgan)
app.use(morgan('dev'));
// Disable views cache
app.set('view cache', false);
}
// config swig template engine
initSwig();
// Request body parsing middleware should be above methodOverride
app.use(bodyParser.urlencoded({
extended: true,
limit: '50mb'
}));
app.use(bodyParser.json({
limit: '50mb'
}));
app.use(methodOverride());
// CookieParser should be above session
app.use(cookieParser());
// Express MongoDB session storage
if (process.env.NODE_ENV === 'secure') {
var cookieSecure = true;
} else {
var cookieSecure = false;
}
app.use(session({
saveUninitialized: true,
resave: true,
secret: config.sessionSecret,
cookie: {
secure: cookieSecure,
httpOnly: true,
maxAge: config.session_max_age
},
// store: new mongoStore({
// mongooseConnection: db,
// collection: config.sessionCollection
// })
store: new RedisStore({
host: process.env.REDIS_HOST || '127.0.0.1',
port: process.env.REDIS_PORT || 6379
})
}));
// Use helmet to secure Express headers
app.use(helmet());
app.disable('x-powered-by');
// Should be placed before express.static
app.use(compress({
filter: function(req, res) {
return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
}
}));
// Setting the app router and static folder
app.use(express.static(path.resolve('./public'), {
maxAge: config.cache_max_age
}));
// Passing checking subdomain middleware
app.use(checkSubDomain);
// Globbing routing files
config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
require(path.resolve(routePath))(app);
});
// Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
app.use(function(err, req, res, next) {
logError(err, res, next);
});
// handle uncaught exception, return status code 500
app.on('uncaughtException', function(req, res, route, err) {
logError(err, res);
});
// Assume 404 since no middleware responded
app.use(function(req, res) {
res.status(404).render('404', {
url: req.originalUrl,
error: 'Not Found'
});
});
// Log SSL usage
console.log('Normal using http protocol port ' + config.port);
var httpServer = http.createServer(app);
// Return HTTPS server instance
return httpServer;
};
function logError(err, res, next) {
// If the error object doesn't exists
if (!err && next) return next();
log.error({
code: Date.now(),
reason: err.stack
});
// Error page
res.status(500).render('500', {
error: err.stack
});
}
function initSwig(developMode) {
swig.setFilter('currency', function(input) {
return numeral(input).format('0,0');
});
swig.setDefaults({
cache: {
get: function(key) {
return swigCache[key];
},
set: function(key, val) {
if (key.indexOf('swig-no-cache-option') === -1) {
swigCache[key] = val;
}
}
}
});
// get city name by code
swig.setFilter('city_name', function(input) {
var cities = require('../app/resources/provinces.json');
var idx = _.findIndex(cities, function(c) {
return c.code === input.toString()
});
if (idx > -1) return cities[idx].name;
return '';
});
}
/**
* Middleware checking subdomain
* @param {[type]} req [description]
* @param {[type]} res [description]
* @param {Function} next [description]
* @return {[type]} [description]
*/
function checkSubDomain(req, res, next) {
var hostUrl = req.headers.host;
if (!hostUrl)
hostUrl = req.get('host');
res.locals.url = req.protocol + '://' + req.headers.host + req.url;
// Invalid request, since headers.host should be set, most likely a malicious attempt
if (!hostUrl) return res.status(400).send();
hostUrl = hostUrl.replace(/^www\./, '').toLowerCase();
var domain = hostUrl.split('.');
var subdomains = req.subdomains || [];
if (subdomains.length > 0 && subdomains[0] === 'www') {
subdomains.splice(0, 1);
}
// lấy domain (sau khi bỏ subdomain)
domain.splice(0, subdomains.length);
req.subDomain = {
host: hostUrl,
domain: domain.join('.').split(':')[0],
domainWithPort: domain.join('.'),
hdvDomain: config.domain,
having: subdomains.length > 0
};
req.subDomain.selfDomain = config.domain.split(':')[0] === req.subDomain.domain;
req.subDomain.store_code = subdomains.length > 0 ? subdomains.join('.') : '';
next();
}
'use strict';
/**
* Module dependencies.
*/
var glob = require('glob'),
chalk = require('chalk');
/**
* Module init function.
*/
module.exports = function() {
/**
* Before we begin, lets set the environment variable
* We'll Look for a valid NODE_ENV variable and if one cannot be found load the production NODE_ENV
*/
var mode = 'production';
if (process.argv.length > 2) {
mode = process.argv[2];
}
var mode = process.env.NODE_ENV || mode;
var environmentFiles = glob.sync('./config/env/' + mode + '.js');
if (environmentFiles && !environmentFiles.length) {
if (process.env.NODE_ENV) {
console.error(chalk.red('No configuration file found for "' + mode + '" environment using production instead'));
} else {
console.error(chalk.red('NODE_ENV is not defined! Using default production environment'));
}
} else {
console.log(chalk.black.bgWhite('Application loaded using the "' + mode + '" environment configuration'));
}
process.env.NODE_ENV = mode;
};
var Hipchatter = require('hipchatter');
var util = require('util');
function BunyanHipchat(options, error) {
options = options || {};
if (!options.token) {
throw new Error('token cannot be null');
} else if (!options.room_name) {
throw new Error('room_name cannot be null');
} else if (!options.room_token) {
throw new Error('room_token cannot be null');
} else {
this.token = options.token;
this.room_name = options.room_name;
this.room_token = options.room_token;
this.color = options.color || 'yellow';
this.message_format = options.message_format || 'html';
this.notify = options.notify || false;
this.error = error || function() {};
this.hipchatter = new Hipchatter(this.token);
}
}
BunyanHipchat.prototype.write = function write(record) {
var self = this,
message;
if (typeof record === 'string') {
record = JSON.parse(record);
}
try {
message = '<b>Hostname:</b> ' + record.hostname + '<br /><b>Thời gian:</b>' + record.time + '<br /><b>Mã lỗi</b>: ' + record.code + '<br /><b>Lý do</b>: ' + record.reason;
} catch (err) {
return self.error(err);
}
var options = {
message: message,
color: self.color,
token: self.room_token,
message_format: self.message_format,
notify: self.notify
};
// self.hipchatter.notify(self.room_name, options, function() {});
};
var config = require('./config'),
bunyan = require('bunyan');
if (config.mode === 'pro') {
var log = bunyan.createLogger({
name: 'PAYMENT_GATE',
streams: [{
stream: process.stdout,
level: 'info'
}, {
level: 'error',
type: 'raw', // use 'raw' to get raw log record objects
stream: new BunyanHipchat({
token: 'XK4Bs30AvRCWfT2Ri9wyBwK8GnSlwxcEGqDnovLv',
room_token: 'Ro3sV5flsdQkCzqwKg4puONrDLHA0zQZ2zZwFBA3',
room_name: 'GOS-LOG'
})
}, {
level: 'error',
path: config.log_file,
type: 'rotating-file',
period: '1d', // daily rotation
count: 10 // keep 10 back copies
}]
});
} else {
var log = bunyan.createLogger({
name: 'POSS Frontend',
streams: [{
stream: process.stdout,
level: 'info'
}, {
stream: process.stderr,
level: 'error',
}]
});
}
exports = module.exports = log;
'use strict';
var redis = require('redis'),
client = redis.createClient();
client.debug_mode = true;
client.on('ready', function() {
console.log('Redis ready.');
});
module.exports = client;
# Deploy PaymentGate (Hai Yen) lên VPS
- **Server:** `123.31.12.55` (Ubuntu 16.04.1 LTS — xem cảnh báo EOL cuối file)
- **Domain:** `pg.haiyenstore.asia`
- **SSH port:** `2222` (không phải 22 mặc định)
- **Stack:** Docker Compose (`app` + `redis`), Nginx (đã cài sẵn) + Certbot (đã cài sẵn)
---
## ⚠️ Server này KHÔNG phải máy trống — đang dùng chung cho nhiều dịch vụ khác
Kiểm tra thực tế (`netstat -tulpn`, `docker ps` ngày 2026-08-28) cho thấy server đang chạy sẵn:
- Docker 20.10.7 + Compose v2.21.0 (đã cài, không cần cài lại) với **~8 docker network/project khác** đang hoạt động.
- Nginx (đã cài, đang phục vụ ít nhất 1 domain khác qua port 80/443/8443) + Apache2 (port 8888/444) song song.
- PM2 chạy ít nhất 1 app Node khác (port 8076/8084/8442).
- Zabbix agent (giám sát hạ tầng — có thể có team khác theo dõi server này).
- Redis độc lập chạy sẵn ở `127.0.0.1:6379` (**không phải Redis của app này** — Redis của app này chạy trong container riêng, không public port, không đụng nhau).
**Hệ quả cho việc deploy:**
- Không cài lại Docker/Nginx/Certbot — chỉ dùng lại.
- Không dùng port `3003` mặc định — đã bị chiếm. Đổi sang `7003` (đã sửa trong `docker-compose.yml`, đã build-test local).
- **Không đụng vào firewall/ufw** — nếu bật ufw với rule tối thiểu sẽ cắt đứt các dịch vụ/port khác đang mở cho các app/client khác trên cùng server. Bỏ hẳn bước này.
- Nginx: chỉ **thêm** 1 file site mới cho `pg.haiyenstore.asia`, không sửa/động vào site có sẵn.
---
## Bước 0 — Chuẩn bị trước khi làm gì trên server
1. **Trỏ DNS**: bản ghi A `pg.haiyenstore.asia``123.31.12.55`.
```bash
dig +short pg.haiyenstore.asia
```
2. **Commit + push code** lên git remote (nếu chưa).
3. **⚠️ Domain khác nhau giữa các cổng thanh toán trong code**`config/env/all.js` hiện hardcode callback riêng cho từng cổng ngoài Epay (Omipay, PVcomBank, FPT Pay, OneFin, Alepay, Appota trỏ `pg.dcvfinance.vn`/`pg.cbotvietnam.com`). Nếu server này cũng phục vụ các cổng đó, cần rà lại — ngoài phạm vi hướng dẫn này.
---
## Bước 1 — SSH vào server (port 2222)
```bash
ssh -p 2222 root@123.31.12.55
```
Có 1 dòng cảnh báo `sudo: unable to resolve host poss` khi chạy `sudo` — vô hại (thiếu entry trong `/etc/hosts`), có thể bỏ qua hoặc sửa nhanh:
```bash
echo "127.0.0.1 poss" | sudo tee -a /etc/hosts
```
**Không cần sửa `/etc/apt/sources.list` sang `old-releases.ubuntu.com` trừ khi thật sự cần `apt install` gói mới** — Docker/Nginx/Certbot đã có sẵn, không cần cài thêm gì cho việc deploy lần này.
---
## Bước 2 — Xác nhận lại Docker/Compose đã sẵn sàng (không cài lại)
```bash
docker --version # Docker version 20.10.7 (đã xác nhận)
docker compose version # v2.21.0 (đã xác nhận)
sudo lsof -i :7003 # PHẢI trống — nếu có ai chiếm, đổi port khác trong docker-compose.yml + nginx config bên dưới
```
---
## Bước 3 — Lấy code
```bash
cd ~
git clone http://git.dcv.vn/anhpt/dcvf-payment-gate-cbot.git payment-gate-hai-yen
cd payment-gate-hai-yen
```
---
## Bước 4 — Tạo `.env` trên server (KHÔNG commit file này)
Từ máy local (chú ý port SSH `-P 2222` viết hoa cho `scp`):
```bash
scp -P 2222 .env root@123.31.12.55:~/payment-gate-hai-yen/.env
```
**Sửa các biến sau cho đúng credential production + domain mới:**
```bash
EPAY_MERCHANT_ID=<merId tht>
EPAY_ENCODE_KEY=<encodeKey tht>
EPAY_DOMAIN=https://pg.megapay.vn
EPAY_REQ_DOMAIN=https://pg.haiyenstore.asia
EPAY_RETURN_URL=https://pg.haiyenstore.asia/epay/return
EPAY_NOTIFY_URL=https://pg.haiyenstore.asia/epay/ipn
```
`EPAY_ENCRYPT_KEY`/`EPAY_DECRYPT_KEY` tự suy từ `EPAY_ENCODE_KEY` mới (không phải MegaPay cấp riêng):
- `EPAY_DECRYPT_KEY` = 24 ký tự **đầu** của `EPAY_ENCODE_KEY`
- `EPAY_ENCRYPT_KEY` = 24 ký tự **cuối** của `EPAY_ENCODE_KEY`
---
## Bước 5 — Build và chạy bằng Docker Compose
```bash
docker compose up -d --build
docker compose ps # cả 2 service phải "Up" / "healthy"
docker compose logs -f app
```
Kiểm tra nội bộ (đúng port mới `7003`):
```bash
curl -sS -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:7003/
```
---
## Bước 6 — Thêm site Nginx mới (không đụng site cũ)
```bash
sudo nano /etc/nginx/sites-available/pg.haiyenstore.asia
```
```nginx
server {
listen 80;
server_name pg.haiyenstore.asia;
location / {
proxy_pass http://127.0.0.1:7003;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
```bash
sudo ln -s /etc/nginx/sites-available/pg.haiyenstore.asia /etc/nginx/sites-enabled/
sudo nginx -t # PHẢI pass — nếu lỗi, kiểm tra không đụng config site khác
sudo systemctl reload nginx
curl -sS -o /dev/null -w "HTTP %{http_code}\n" http://pg.haiyenstore.asia/
```
---
## Bước 7 — Bật HTTPS bằng Certbot (đã cài sẵn, chỉ chạy)
```bash
sudo certbot --nginx -d pg.haiyenstore.asia
curl -sS -o /dev/null -w "HTTP %{http_code}\n" https://pg.haiyenstore.asia/
```
---
## Bước 8 — Smoke test sau khi deploy
```bash
curl -sS -X POST https://pg.haiyenstore.asia/epay/create-payment-url \
-H "Content-Type: application/json" \
-d '{"agentCode":"DCVF","fullName":"NGUYEN VAN A","amount":"10000"}'
```
Kiểm tra response: `domain` phải là `https://pg.megapay.vn` (production, không phải sandbox), `callBackUrl`/`notiUrl` phải là `https://pg.haiyenstore.asia/...`. Sai thì kiểm tra lại `.env` rồi `docker compose restart app`.
**Credential đã là production thật** — không dùng thẻ test trong tài liệu MegaPay nữa (nếu thẻ test vẫn "thanh toán được" nghĩa là đang trỏ nhầm sandbox). Test bằng giao dịch thật số tiền nhỏ hoặc theo hướng dẫn UAT riêng của MegaPay.
---
## Bước 9 — Đảm bảo container tự khởi động lại
`restart: unless-stopped` đã có sẵn trong `docker-compose.yml` — container tự chạy lại khi Docker daemon khởi động (kể cả sau reboot), miễn Docker daemon tự chạy khi boot (thường mặc định đã bật sẵn, vì server này đã dùng Docker từ trước). Xác nhận:
```bash
systemctl is-enabled docker
```
**Không nên tự `reboot` server này để test** — đang có nhiều dịch vụ khác chạy chung, reboot ảnh hưởng tất cả. Nếu cần test container tự hồi phục, chỉ nên:
```bash
docker compose restart app
```
---
## Cập nhật code sau này
```bash
cd ~/payment-gate-hai-yen
git pull
docker compose up -d --build
```
---
## Troubleshooting thường gặp
| Triệu chứng | Nguyên nhân thường gặp | Cách kiểm tra |
|---|---|---|
| `docker compose up` báo port đã dùng | Port `7003` bị ai đó chiếm sau này | `sudo lsof -i :7003`, đổi port khác trong `docker-compose.yml` + nginx config |
| `docker compose ps` báo app không "healthy" | Thiếu biến `.env`, Redis chưa sẵn sàng | `docker compose logs app` |
| `sudo nginx -t` báo lỗi | Có thể do đụng site config khác, không phải do file mới | Đọc kỹ dòng lỗi, dòng có ghi rõ file/dòng gây lỗi |
| Certbot fail "Could not find domain" | DNS chưa trỏ đúng/chưa lan truyền | `dig +short pg.haiyenstore.asia` |
| `502 Bad Gateway` từ Nginx | App container chưa chạy/crash, hoặc sai port trong nginx config | `docker compose ps`, `docker compose logs app` |
| callBackUrl vẫn ra domain cũ | `.env` sai `EPAY_REQ_DOMAIN`/`EPAY_RETURN_URL`/`EPAY_NOTIFY_URL` | `docker compose exec app env \| grep EPAY` |
| Response vẫn ra `domain: sandbox.megapay.vn` | `.env` chưa có/sai `EPAY_DOMAIN` | `docker compose exec app env \| grep EPAY_DOMAIN` |
| Dịch vụ KHÁC trên server đột nhiên lỗi sau khi deploy | Trùng port/tài nguyên với app này | So port đang dùng trước/sau bằng `sudo lsof -i` — không nên xảy ra nếu làm đúng bước 2 |
---
## Còn treo lại
- Ubuntu 16.04 đã EOL từ 04/2021, không còn vá bảo mật — Docker/Nginx/Certbot vẫn chạy được (đã xác nhận có sẵn) nhưng bản thân OS là rủi ro dài hạn, nên nâng cấp/đổi VPS khi có điều kiện.
- Các cổng khác (Omipay, PVcomBank, FPT Pay, OneFin, Alepay, Appota) vẫn hardcode domain `pg.dcvfinance.vn`/`pg.cbotvietnam.com` trong `config/env/all.js`.
- Secret cũ từng nằm plaintext trong lịch sử git — nên rotate key thật với từng nhà cung cấp trước khi coi đây là production chính thức lâu dài.
- Server dùng chung nhiều client/dịch vụ khác — bất kỳ thay đổi ở tầng hệ thống (firewall, Nginx global config, reboot...) đều cần cẩn trọng vì ảnh hưởng ngoài phạm vi app này.
services:
app:
build: .
restart: unless-stopped
env_file: .env
environment:
NODE_ENV: production
REDIS_HOST: redis
REDIS_PORT: 6379
ports:
# Host port 7003 chosen because this VPS is shared with many other
# services; 3003 (and most of the 3000-3003/8080-8090/9000-9080 range)
# was already taken. Verify with `sudo lsof -i :7003` before reusing.
- "7003:3003"
depends_on:
- redis
volumes:
- ./log:/app/log
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis-data:/data
volumes:
redis-data:
'use strict';
module.exports = function(grunt) {
var path = require('path');
// Unified Watch Object
var watchFiles = {
serverViews: ['app/views/**/*.*'],
serverJS: ['gruntfile.js', 'server.js', 'config/**/*.js', 'app/**/*.js'],
clientCss: ['public/css/*.css','!public/css/*.min.css']
};
// Project Configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch: {
serverViews: {
files: watchFiles.serverViews,
options: {
livereload: false
}
},
serverJS: {
files: watchFiles.serverJS
},
clientCss: {
files: watchFiles.clientCss,
tasks: ['cssmin']
}
},
nodemon: {
dev: {
script: 'server.js',
options: {
ext: 'js,html',
watch: watchFiles.serverViews.concat(watchFiles.serverJS),
ignore: ['node_modules/**']
}
}
},
concurrent: {
default: ['cssmin', 'nodemon:dev', 'watch'],
options: {
logConcurrentOutput: true,
limit: 10
}
},
env: {
dev: {
NODE_ENV: 'development'
},
test: {
NODE_ENV: 'test'
},
secure: {
NODE_ENV: 'secure'
},
pro: {
NODE_ENV: 'production'
}
},
cssmin: {
target: {
files: [{
expand: true,
cwd: 'public/css',
src: ['*.css', '!*.min.css'],
dest: 'public/css',
ext: '.min.css'
}]
}
}
});
// Load NPM tasks
require('load-grunt-tasks')(grunt);
// Making grunt default to force in order not to break the project.
grunt.option('force', true);
// Default task(s).
grunt.registerTask('default', ['env:dev', 'concurrent:default']);
// Debug task.
grunt.registerTask('debug', ['env:dev', 'concurrent:debug']);
// Secure task(s).
grunt.registerTask('production', ['env:pro', 'concurrent:default']);
// Lint task(s).
grunt.registerTask('lint', ['env:dev', 'jshint', 'csslint']);
};
// Karma configuration
// Generated on Thu Sep 24 2015 11:25:10 GMT+0700 (SE Asia Standard Time)
module.exports = function(config) {
config.set({
basePath: 'test',
frameworks: ['jasmine', 'requirejs'],
files: [
'node_modules/angular/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'./src/**/*.js',
'./test/**/*.js'
],
exclude: [
],
preprocessors: {
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
captureTimeout: 60000,
singleRun: false
});
}
# Ignore everything in this directory
*
# Except this file
!.gitignore
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "PAYMENT-GATE",
"description": "PAYMENTGATE",
"version": "0.0.1",
"author": "AnhPT",
"engines": {
"node": "6.11.1",
"npm": "3.10.10"
},
"scripts": {
"start": "nodemon --ignore node_modules/ --ignore public/ server.js development",
"test": "grunt test",
"css": "grunt default",
"postinstall": "bower install --config.interactive=false"
},
"dependencies": {
"async": "^2.0.1",
"body-parser": "^1.9.3",
"bunyan": "^1.8.1",
"chalk": "^0.5.1",
"compression": "^1.6.2",
"connect-redis": "^2.2.0",
"consolidate": "^0.10.0",
"cookie-parser": "^1.4.1",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"crypto-js": "^4.1.1",
"dateformat": "^3.0.3",
"dotenv": "^17.4.2",
"express": "^4.14.0",
"express-session": "^1.13.0",
"glob": "^7.2.3",
"he": "^0.5.0",
"helmet": "^2.1.1",
"hipchatter": "^0.2.0",
"http": "0.0.0",
"https-localhost": "^4.4.1",
"jsonwebtoken": "^9.0.2",
"lodash": "^3.6.0",
"lodash.samplesize": "^4.2.0",
"materialize-stepper": "^3.1.0",
"md5": "^2.3.0",
"method-override": "~2.3.0",
"moment": "^2.9.0",
"mongoose": "^5.7.12",
"morgan": "^1.4.1",
"node-rsa": "^1.1.1",
"nodemon": "^2.0.2",
"numeral": "^1.5.3",
"qs": "^6.9.0",
"redis": "^2.6.2",
"request": "^2.55.0",
"sendgrid": "^5.2.3",
"serve-favicon": "^2.3.0",
"sha256": "^0.2.0",
"swig": "^1.4.2",
"uuid": "2.0.1",
"validator": "~4.3.0"
},
"devDependencies": {
"grunt": "^1.0.4",
"grunt-cli": "^1.3.2",
"grunt-concurrent": "~1.0.0",
"grunt-contrib-cssmin": "~0.10.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-env": "~0.4.1",
"grunt-nodemon": "~0.3.0",
"load-grunt-tasks": "~1.0.0"
}
}
EEaRFiXmWJvAnaFyEas7QNrAumWYE5weXXwl55eBYU
\ No newline at end of file
/*
* bootstrap-tagsinput v0.8.0
*
*/
.bootstrap-tagsinput {
background-color: #f2f5fa;
border: 1px solid #ccc;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
display: inline-block;
padding: 10px 6px;
color: #555;
vertical-align: middle;
border-radius: 4px;
width: 100%;
line-height: 14px;
cursor: text;
height: 40px;
}
.bootstrap-tagsinput input {
border: none;
box-shadow: none;
outline: none;
background-color: transparent;
padding: 0 6px;
margin: 0;
width: auto;
max-width: inherit;
color: black !important;
display: block;
}
.bootstrap-tagsinput.form-control input::-moz-placeholder {
color: #777;
opacity: 1;
}
.bootstrap-tagsinput.form-control input:-ms-input-placeholder {
color: #777;
}
.bootstrap-tagsinput.form-control input::-webkit-input-placeholder {
color: #777;
}
.bootstrap-tagsinput input:focus {
border: none;
box-shadow: none;
}
.bootstrap-tagsinput .tag {
margin-right: 2px;
color: white !important;
background: #2196F3;
}
.bootstrap-tagsinput .tag [data-role="remove"] {
margin-left: 8px;
cursor: pointer;
color: white !important;
}
.bootstrap-tagsinput .tag [data-role="remove"]:after {
content: "x";
padding: 0px 2px;
}
.bootstrap-tagsinput .tag [data-role="remove"]:hover {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.bootstrap-tagsinput .tag [data-role="remove"]:hover:active {
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.bootstrap-tagsinput .twitter-typeahead {
display: contents;
}
.bootstrap-tagsinput .tt-dataset {
border: 1px solid #ccc;
}
.bootstrap-tagsinput .tt-dataset .tt-suggestion {
padding: 10px;
background: white;
color: black !important;
}
.bootstrap-tagsinput .tt-dataset .tt-suggestion:hover {
background: #2196F3;
}
\ No newline at end of file
/* here you can put your own css to customize and override the theme */
.container-banner {
color: #fff;
font-family: Raleway;
font-weight: 500;
line-height: 73px;
margin: 17px 0;
text-transform: none;
}
.category {
border: 1px solid #F0F1F2;
padding: 25px 35px;
font-family: 'Open Sans', sans-serif;
min-height: 300px;
}
.category a {
text-decoration: none;
}
.category li {
margin-bottom: 5px;
}
.category-header {
color: #606873;
line-height: 25px;
margin: 20px 0;
text-decoration: none;
}
.category-header a {
color: #606873;
text-decoration: none;
}
.category-content a {
color: #A3A8AF;
}
.category-content a:hover {
color: #3FD6FF;
}
.rmenu {
padding: 0px;
margin-top: 36px;
}
.rmenu ul {
padding: 0px;
margin-bottom: 40px;
}
.rmenu ul>li {
background: #2a3b4d;
border-bottom: 1px solid #354556;
}
.rmenu ul>li:hover {
background: #23303E;
color: #fff;
border-left: 3px solid #3FD6FF;
}
.rmenu li>p {
padding: 30px 10px;
text-transform: uppercase;
font-size: 15px;
margin: 0px;
}
.rmenu p>a {
padding: 30px 110px 25px 10px;
color: #8a95a6;
text-decoration: none;
}
.rmenu p>a:hover {
color: #fff;
}
.rcontent {
margin-left: 20px;
padding-bottom: 40px;
}
.rcontent h1 {
text-align: left;
}
.rlist {
padding-left: 20px;
}
.rlist p {
margin: 0;
padding: 5px 20px;
}
.rlist span {
margin-right: 10px;
}
.rlist i {
color: #5A5959;
}
.rlist a {
font-size: 16px;
color: #10B2D0;
text-decoration: none;
}
.rlist a:hover {
color: #3FD6FF;
}
.rlist .panel-body {
height: 250px;
overflow: scroll;
background: #f3f3f3;
}
.dmenu .panel-heading {
background-color: #2a3b4d;
color: #8a95a6;
}
.dmenu .panel-heading a {
padding-left: 7px;
}
.dmenu .panel-body a {
font-size: 15px;
}
@media only screen and (max-width: 767px) {
.section-heading h1 {
font-size: 20px !important;
}
.list-unstyled h2 {
font-size: 18px !important;
}
.list-unstyled li p {
font-size: 16px;
font-weight: 400;
}
h2 {
font-size: 18px !important;
padding-top: 25px;
}
.row.clearfix p {
font-size: 18px !important;
}
}
/* header */
.recruitment-header {
width: 100%;
float: left;
background: url("/img/tuyendung.png");
height: 300px;
background-size: cover;
background-repeat: no-repeat;
position: relative;
overflow: hidden;
}
.recruitment-header::before {
content: "";
width: 50%;
height: 221%;
position: absolute;
top: 0;
left: 0;
background: #0093dd78 !important;
transform: rotate(23deg);
-webkit-transform: rotate(23deg);
transform-origin: 100% 0;
-webkit-transform-origin: 100% 0;
}
.recruitment-header .container .row.title {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
position: absolute;
margin: auto;
}
@media only screen and (max-width: 767px) {
.recruitment-header .container .row.title {
width: 100%;
}
.recruitment-header::before {
width: 135%;
height: 221%;
}
}
.recruitment-header .container .row h1 {
content: attr(data-title);
margin-top: 0;
text-transform: uppercase;
color: #fff;
z-index: 0;
position: relative;
}
/* title */
.recruitment-title {
width: 100%;
float: left;
}
.recruitment-title .container .row .block-title {
border-left: 3px solid #e27a17;
padding-left: 20px;
margin-bottom: 45px;
}
.recruitment-title .container .row .block-title h2 {
color: #333;
font-family: "Oswald", sans-serif;
}
.recruitment-title .container .row .block-title p.italic {
font-style: italic;
}
/* content */
.recruitment-content {
width: 100%;
float: left;
}
.recruitment-content .container .row .gallery .mb-3 {
width: 33.33333%;
float: left;
height: 250px;
}
@media only screen and (max-width: 767px) {
.recruitment-content .container .row .gallery .mb-3 {
width: 100%;
float: left;
}
.recruitment-content .container .row .gallery .mb-3 p.position a{
font-size: 21px !important;
}
.recruitment-title .container .row .block-title h2 {
font-size: 24px;
}
}
.recruitment-content .container .row .gallery .mb-3 p.position a {
color: #0093dd;
font-size: 18px;
line-height: 26px;
font-weight: 500;
}
.recruitment-content .container .row .gallery .mb-3 button.btn-more {
background: #e27a17;
border: none;
display: inline-block;
padding: 9px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
border-radius: 20px;
width: 120px;
outline: none;
}
.recruitment-content .container .row .gallery .mb-3 button.btn-more a {
color: #fff;
text-decoration: none;
font-weight: 500;
text-transform: uppercase;
}
/* filter */
.recruitment-filter {
width: 100%;
float: left;
margin-bottom: 30px;
}
.recruitment-filter .container .row .col-md-12 button {
background: #0093dd;
color: #fff;
border: 2px solid #0093dd;
border-radius: 20px;
outline: none;
}
.recruitment-filter .container .row .col-md-12 button:hover {
background: #e27a17;
color: #fff;
border: 2px solid #e27a17;
border-radius: 20px;
outline: none;
}
/* gallery */
.gallery {
width: 100%;
float: left;
padding: 20px;
}
.gallery .pics {
-webkit-transition: all 350ms ease;
transition: all 350ms ease;
}
.gallery .animation {
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
}
@media (max-width: 450px) {
.gallery {
-webkit-column-count: 1;
-moz-column-count: 1;
column-count: 1;
-webkit-column-width: 100%;
-moz-column-width: 100%;
column-width: 100%;
}
}
@media (max-width: 400px) {
.btn.filter {
padding-left: 1.1rem;
padding-right: 1.1rem;
}
}
a:hover {
text-decoration: none;
}
.section-prefooter,
.section-footer {
width: 100%;
float: left;
}
a {
color: #3FD6FF;
}
a:hover {
color: #3FD6FF;
}
.pre-header a:hover {
color: #3FD6FF;
}
.shop-currencies a.current {
color: #3FD6FF;
}
.header-navigation ul > li.active > a,
.header-navigation ul > li > a:hover,
.header-navigation ul > li > a:focus,
.header-navigation ul > li.open > a,
.header-navigation ul > li.open > a:hover,
.header-navigation ul > li.open > a:focus {
color: #3FD6FF;
}
.header-navigation li.menu-search i:hover {
color: #3FD6FF;
}
.sidebar a:hover {
color: #3FD6FF;
}
.sidebar .dropdown.open .dropdown-toggle:hover {
color: #3FD6FF;
}
.sidebar-menu .dropdown-menu li > a:hover, .sidebar-menu .dropdown-menu li > a:focus, .sidebar-menu li.active > a, .sidebar-menu li.active > a:hover {
color: #3FD6FF;
}
.content-page a:hover,
.sidebar2 a:hover {
color: #3FD6FF;
}
.content-page .link, .content-page .link:hover, .content-page .link:active {
color: #3FD6FF;
}
.page-404 .number,
.page-500 .number {
color: #3FD6FF;
}
.content-form-page a:hover {
color: #3FD6FF;
}
.quote-v1 a.btn-transparent:hover {
background: #3FD6FF;
}
.recent-work h2 a:hover {
color: #3FD6FF;
}
.recent-work .recent-work-item .fa:hover {
color: #3FD6FF;
}
.our-clients h2 a:hover {
color: #3FD6FF;
}
.front-team h3 strong {
color: #3FD6FF;
}
.ecommerce .header-navigation ul > li.active > a,
.ecommerce .header-navigation ul > li > a:hover,
.ecommerce .header-navigation ul > li > a:focus,
.ecommerce .header-navigation ul > li.open > a,
.ecommerce .header-navigation ul > li.open > a:hover,
.ecommerce .header-navigation ul > li.open > a:focus {
color: #3FD6FF;
}
.product-item h3 a:hover {
color: #3FD6FF;
}
.checkout-page a:hover {
color: #3FD6FF;
}
.langs-block-others:after {
border-bottom: 8px solid #3FD6FF;
}
.header-navigation > ul > li.dropdown:hover > a:after {
border-bottom: 8px solid #3FD6FF;
}
.header-navigation .dropdown-menu > li > a:hover,
.header-navigation .dropdown-menu > li.active > a,
.header-navigation .header-navigation-content .header-navigation-col li > a:hover,
.header-navigation .header-navigation-content .header-navigation-col li.active > a {
background: #3FD6FF;
color: #fff;
}
.header-navigation .dropdown-menu .header-navigation-content-ext li > a:hover,
.header-navigation .dropdown-menu .header-navigation-content-ext li.active > a {
background: #fff;
color: #3FD6FF;
}
.header-navigation .search-box:after {
border-bottom: 8px solid #3FD6FF;
}
.header-navigation .search-box {
border-top: solid 2px #3FD6FF;
}
.title-wrapper h1 span {
color: #3FD6FF;
}
.breadcrumb > .active {
color: #3FD6FF;
}
.form-info h2 em {
color: #3FD6FF;
}
.nav-tabs {
border-color: #3FD6FF;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #3FD6FF;
}
.content-search h1 em {
color: #3FD6FF;
}
.recent-work .recent-work-item:hover a.recent-work-description {
background: #3FD6FF;
}
.testimonials-v1 blockquote:after {
background-color: #3FD6FF;
}
.testimonials-v1 span.testimonials-name {
color: #3FD6FF;
}
.search-result-item h4 a {
color: #3FD6FF;
}
.top-cart-content:after {
border-bottom: 8px solid #3FD6FF;
}
.goods-data a,
.checkout-page .checkout-description a {
color: #3FD6FF;
}
.product-page .review a {
color: #3FD6FF;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #3FD6FF;
color: #fff;
}
.list-view-sorting a {
background: #fff;
color: #3FD6FF;
}
::-moz-selection {
color: #fff;
background: #3FD6FF;
}
::selection {
color: #fff;
background: #3FD6FF;
}
.steps-block ::-moz-selection {
color: #3FD6FF;
background: #fff;
}
.steps-block ::selection {
color: #3FD6FF;
background: #fff;
}
.owl-buttons .owl-prev:hover {
background-color: #3FD6FF;
}
.owl-buttons .owl-next:hover {
background-color: #3FD6FF;
}
.steps-block-red {
background: #3FD6FF;
}
.pre-footer .photo-stream img:hover {
border-color: #3FD6FF;
}
.pre-footer-light dl.f-twitter dd a {
color: #3FD6FF;
}
.pre-footer-light address a {
color: #3FD6FF;
}
.testimonials-v1 .left-btn:hover {
background-color: #3FD6FF;
}
.testimonials-v1 .right-btn:hover {
background-color: #3FD6FF;
}
.blog-tags li i,
.blog-info li i {
color: #3FD6FF;
}
.blog-posts .more,
.blog-sidebar .more {
color: #3FD6FF;
}
.recent-news h3 a {
color: #3FD6FF;
}
.blog-photo-stream li img:hover {
border-color: #3FD6FF;
}
.blog-tags li a:hover {
color: #fff;
background: #3FD6FF;
}
.blog-tags li a:hover:after {
border-left-color: #3FD6FF;
}
.sidebar-categories li > a:hover,
.sidebar-categories li.active > a,
.sidebar-categories li.active:hover > a {
color: #3FD6FF;
}
.blog-item blockquote {
border-color: #3FD6FF;
}
.blog-item h4.media-heading span a {
color: #3FD6FF;
}
.front-steps-wrapper .front-step1 {
background: #3FD6FF;
}
.pricing-active {
border: 3px solid #3FD6FF;
box-shadow: 7px 7px rgba(100, 174, 217, 0.2);
}
.pricing:hover {
border: 3px solid #3FD6FF;
}
.pricing:hover h4 {
color: #3FD6FF;
}
.pricing-head h3 {
background: #3FD6FF;
}
.pricing-head-active h4 {
color: #3FD6FF;
}
.pricing-content li i {
color: #3FD6FF;
}
.top-cart-block .fa-shopping-cart {
background: #3FD6FF;
}
.product-item .btn:hover {
background: #3FD6FF;
}
.pi-price {
color: #3FD6FF;
}
.product-item .add2cart:hover {
color: #fff !important;
background: #3FD6FF !important;
border-color: #3FD6FF;
}
.goods-page-price strong,
.goods-page-total strong,
.checkout-price strong,
.checkout-total strong {
color: #3FD6FF;
}
.shopping-total strong,
.checkout-total-block strong {
color: #3FD6FF;
}
.compare-item strong {
color: #3FD6FF;
}
.sidebar-products .price {
color: #3FD6FF;
}
.price-availability-block .price strong {
color: #3FD6FF;
}
.require {
color: #3FD6FF;
}
.content-form-page .form-control:focus {
border: solid 1px #3FD6FF;
}
.content-search input:focus {
border: solid 1px #3FD6FF;
}
.btn-primary {
background: #3FD6FF;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
background: #3FD6FF !important;
}
.header-navigation .dropdown-menu > li:first-child {
border-top: 2px solid #3FD6FF;
}
.front-steps-wrapper .front-step1:after {
border-left: 15px solid #83BEE0;
}
.del-goods:hover,
.add-goods:hover {
background-color: #83BEE0;
}
.sidebar a:hover > .fa-angle-down {
background-position: -22px 0;
}
.sidebar .collapsed:hover > .fa-angle-down {
background-position: -22px -37px;
}
.top-cart-content {
border-top: solid 2px #83BEE0;
}
.front-skills .progress-bar {
background: #83BEE0;
}
.service-box-v1:hover {
background: #83BEE0;
}
.header .mobi-toggler:hover {
background-color: #83BEE0;
border-color: #83BEE0;
}
@media (max-width: 1024px) {
.header .header-navigation li > a:hover,
.header .header-navigation li.active > a,
.header .header-navigation li.open > a:hover {
color: #83BEE0 !important;
}
}
.faq-tabbable {
}
.faq-tabbable li:hover a,
.faq-tabbable li.active a{
}
.faq-tabbable li.active:after {
border-left: 6px solid #83BEE0;
}
.mix-filter li:hover, .mix-filter li.active {
background: #83BEE0;
color: #fff;
}
.mix-grid .mix .mix-details {
background: #83BEE0;
}
.mix-grid .mix a.mix-link,
.mix-grid .mix a.mix-preview {
background: #1ab8db;
}
.langs-block-others {
border-top: solid 2px #83BEE0;
}
.brands .owl-buttons .owl-prev:hover {
background-position: 18px -325px;
}
.brands .owl-buttons .owl-next:hover {
background-position: -249px -325px;
}
.header-navigation ul > li.active > a/*,
.ecommerce .header-navigation ul > li.active > a*/ {
border-bottom: 2px solid #83BEE0;
}
td.text-info{
color: #3FD6FF !important;
}
\ No newline at end of file
a {
color: #798b97;
}
a:hover {
color: #798b97;
}
.pre-header a:hover {
color: #798b97;
}
.shop-currencies a.current {
color: #798b97;
}
.header-navigation ul > li.active > a,
.header-navigation ul > li > a:hover,
.header-navigation ul > li > a:focus,
.header-navigation ul > li.open > a,
.header-navigation ul > li.open > a:hover,
.header-navigation ul > li.open > a:focus {
color: #798b97;
}
.header-navigation li.menu-search i:hover {
color: #798b97;
}
.sidebar a:hover {
color: #798b97;
}
.sidebar .dropdown.open .dropdown-toggle:hover {
color: #798b97;
}
.sidebar-menu .dropdown-menu li > a:hover, .sidebar-menu .dropdown-menu li > a:focus, .sidebar-menu li.active > a, .sidebar-menu li.active > a:hover {
color: #798b97;
}
.content-page a:hover,
.sidebar2 a:hover {
color: #798b97;
}
.content-page .link, .content-page .link:hover, .content-page .link:active {
color: #798b97;
}
.page-404 .number,
.page-500 .number {
color: #798b97;
}
.content-form-page a:hover {
color: #798b97;
}
.quote-v1 a.btn-transparent:hover {
background: #798b97;
}
.recent-work h2 a:hover {
color: #798b97;
}
.recent-work .recent-work-item .fa:hover {
color: #798b97;
}
.our-clients h2 a:hover {
color: #798b97;
}
.front-team h3 strong {
color: #798b97;
}
.ecommerce .header-navigation ul > li.active > a,
.ecommerce .header-navigation ul > li > a:hover,
.ecommerce .header-navigation ul > li > a:focus,
.ecommerce .header-navigation ul > li.open > a,
.ecommerce .header-navigation ul > li.open > a:hover,
.ecommerce .header-navigation ul > li.open > a:focus {
color: #798b97;
}
.product-item h3 a:hover {
color: #798b97;
}
.checkout-page a:hover {
color: #798b97;
}
.langs-block-others:after {
border-bottom: 8px solid #798b97;
}
.header-navigation > ul > li.dropdown:hover > a:after {
border-bottom: 8px solid #798b97;
}
.header-navigation .dropdown-menu > li > a:hover,
.header-navigation .dropdown-menu > li.active > a,
.header-navigation .header-navigation-content .header-navigation-col li > a:hover,
.header-navigation .header-navigation-content .header-navigation-col li.active > a {
background: #798b97;
color: #fff;
}
.header-navigation .dropdown-menu .header-navigation-content-ext li > a:hover,
.header-navigation .dropdown-menu .header-navigation-content-ext li.active > a {
background: #fff;
color: #798b97;
}
.header-navigation .search-box:after {
border-bottom: 8px solid #798b97;
}
.header-navigation .search-box {
border-top: solid 2px #798b97;
}
.title-wrapper h1 span {
color: #798b97;
}
.breadcrumb > .active {
color: #798b97;
}
.form-info h2 em {
color: #798b97;
}
.nav-tabs {
border-color: #798b97;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #798b97;
}
.content-search h1 em {
color: #798b97;
}
.recent-work .recent-work-item:hover a.recent-work-description {
background: #798b97;
}
.testimonials-v1 blockquote:after {
background-color: #798b97;
}
.testimonials-v1 span.testimonials-name {
color: #798b97;
}
.search-result-item h4 a {
color: #798b97;
}
.top-cart-content:after {
border-bottom: 8px solid #798b97;
}
.goods-data a,
.checkout-page .checkout-description a {
color: #798b97;
}
.product-page .review a {
color: #798b97;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #798b97;
color: #fff;
}
.list-view-sorting a {
background: #fff;
color: #798b97;
}
::-moz-selection {
color: #fff;
background: #798b97;
}
::selection {
color: #fff;
background: #798b97;
}
.steps-block ::-moz-selection {
color: #798b97;
background: #fff;
}
.steps-block ::selection {
color: #798b97;
background: #fff;
}
.owl-buttons .owl-prev:hover {
background-color: #798b97;
}
.owl-buttons .owl-next:hover {
background-color: #798b97;
}
.steps-block-red {
background: #798b97;
}
.pre-footer .photo-stream img:hover {
border-color: #798b97;
}
.pre-footer-light dl.f-twitter dd a {
color: #798b97;
}
.pre-footer-light address a {
color: #798b97;
}
.testimonials-v1 .left-btn:hover {
background-color: #798b97;
}
.testimonials-v1 .right-btn:hover {
background-color: #798b97;
}
.blog-tags li i,
.blog-info li i {
color: #798b97;
}
.blog-posts .more,
.blog-sidebar .more {
color: #798b97;
}
.recent-news h3 a {
color: #798b97;
}
.blog-photo-stream li img:hover {
border-color: #798b97;
}
.blog-tags li a:hover {
color: #fff;
background: #798b97;
}
.blog-tags li a:hover:after {
border-left-color: #798b97;
}
.sidebar-categories li > a:hover,
.sidebar-categories li.active > a,
.sidebar-categories li.active:hover > a {
color: #798b97;
}
.blog-item blockquote {
border-color: #798b97;
}
.blog-item h4.media-heading span a {
color: #798b97;
}
.front-steps-wrapper .front-step1 {
background: #798b97;
}
.pricing-active {
border: 3px solid #798b97;
box-shadow: 7px 7px rgba(121, 139, 151, 0.2);
}
.pricing:hover {
border: 3px solid #798b97;
}
.pricing:hover h4 {
color: #798b97;
}
.pricing-head h3 {
background: #798b97;
}
.pricing-head-active h4 {
color: #798b97;
}
.pricing-content li i {
color: #798b97;
}
.top-cart-block .fa-shopping-cart {
background: #798b97;
}
.product-item .btn:hover {
background: #798b97;
}
.pi-price {
color: #798b97;
}
.product-item .add2cart:hover {
color: #fff !important;
background: #798b97 !important;
border-color: #798b97;
}
.goods-page-price strong,
.goods-page-total strong,
.checkout-price strong,
.checkout-total strong {
color: #798b97;
}
.shopping-total strong,
.checkout-total-block strong {
color: #798b97;
}
.compare-item strong {
color: #798b97;
}
.sidebar-products .price {
color: #798b97;
}
.price-availability-block .price strong {
color: #798b97;
}
.require {
color: #798b97;
}
.content-form-page .form-control:focus {
border: solid 1px #798b97;
}
.content-search input:focus {
border: solid 1px #798b97;
}
.btn-primary {
background: #798b97;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
background: #798b97;
}
.header-navigation .dropdown-menu > li:first-child {
border-top: 2px solid #798b97;
}
.front-steps-wrapper .front-step1:after {
border-left: 15px solid #93A2AB;
}
.del-goods:hover,
.add-goods:hover {
background-color: #93A2AB;
}
.sidebar a:hover > .fa-angle-down {
background-position: -66px 0;
}
.sidebar .collapsed:hover > .fa-angle-down {
background-position: -66px -37px;
}
.top-cart-content {
border-top: solid 2px #93A2AB;
}
.front-skills .progress-bar {
background: #93A2AB;
}
.service-box-v1:hover {
background: #93A2AB;
}
.header .mobi-toggler:hover {
background-color: #93A2AB;
border-color: #93A2AB;
}
@media (max-width: 1024px) {
.header .header-navigation li > a:hover,
.header .header-navigation li.active > a,
.header .header-navigation li.open > a:hover {
color: #93A2AB !important;
}
}
.faq-tabbable {
border-left: solid 2px #93A2AB;
}
.faq-tabbable li:hover a,
.faq-tabbable li.active a{
background: #93A2AB;
}
.faq-tabbable li.active:after {
border-left: 6px solid #93A2AB;
}
.mix-filter li:hover, .mix-filter li.active {
background: #93A2AB;
color: #fff;
}
.mix-grid .mix .mix-details {
background: #93A2AB;
}
.mix-grid .mix a.mix-link,
.mix-grid .mix a.mix-preview {
background: #959798;
}
.langs-block-others {
border-top: solid 2px #93A2AB;
}
.brands .owl-buttons .owl-prev:hover {
background-position: 18px -753px;
}
.brands .owl-buttons .owl-next:hover {
background-position: -249px -753px;
}
.header-navigation ul > li.active > a/*,
.ecommerce .header-navigation ul > li.active > a*/ {
border-bottom: 2px solid #93A2AB;
}
\ No newline at end of file
a {
color: #67bd3c;
}
a:hover {
color: #67bd3c;
}
.pre-header a:hover {
color: #67bd3c;
}
.shop-currencies a.current {
color: #67bd3c;
}
.header-navigation ul > li.active > a,
.header-navigation ul > li > a:hover,
.header-navigation ul > li > a:focus,
.header-navigation ul > li.open > a,
.header-navigation ul > li.open > a:hover,
.header-navigation ul > li.open > a:focus {
color: #67bd3c;
}
.header-navigation li.menu-search i:hover {
color: #67bd3c;
}
.sidebar a:hover {
color: #67bd3c;
}
.sidebar .dropdown.open .dropdown-toggle:hover {
color: #67bd3c;
}
.sidebar-menu .dropdown-menu li > a:hover, .sidebar-menu .dropdown-menu li > a:focus, .sidebar-menu li.active > a, .sidebar-menu li.active > a:hover {
color: #67bd3c;
}
.content-page a:hover,
.sidebar2 a:hover {
color: #67bd3c;
}
.content-page .link, .content-page .link:hover, .content-page .link:active {
color: #67bd3c;
}
.page-404 .number,
.page-500 .number {
color: #67bd3c;
}
.content-form-page a:hover {
color: #67bd3c;
}
.quote-v1 a.btn-transparent:hover {
background: #67bd3c;
}
.recent-work h2 a:hover {
color: #67bd3c;
}
.recent-work .recent-work-item .fa:hover {
color: #67bd3c;
}
.our-clients h2 a:hover {
color: #67bd3c;
}
.front-team h3 strong {
color: #67bd3c;
}
.ecommerce .header-navigation ul > li.active > a,
.ecommerce .header-navigation ul > li > a:hover,
.ecommerce .header-navigation ul > li > a:focus,
.ecommerce .header-navigation ul > li.open > a,
.ecommerce .header-navigation ul > li.open > a:hover,
.ecommerce .header-navigation ul > li.open > a:focus {
color: #67bd3c;
}
.product-item h3 a:hover {
color: #67bd3c;
}
.checkout-page a:hover {
color: #67bd3c;
}
.langs-block-others:after {
border-bottom: 8px solid #67bd3c;
}
.header-navigation > ul > li.dropdown:hover > a:after {
border-bottom: 8px solid #67bd3c;
}
.header-navigation .dropdown-menu > li > a:hover,
.header-navigation .dropdown-menu > li.active > a,
.header-navigation .header-navigation-content .header-navigation-col li > a:hover,
.header-navigation .header-navigation-content .header-navigation-col li.active > a {
background: #67bd3c;
color: #fff;
}
.header-navigation .dropdown-menu .header-navigation-content-ext li > a:hover,
.header-navigation .dropdown-menu .header-navigation-content-ext li.active > a {
background: #fff;
color: #67bd3c;
}
.header-navigation .search-box:after {
border-bottom: 8px solid #67bd3c;
}
.header-navigation .search-box {
border-top: solid 2px #67bd3c;
}
.title-wrapper h1 span {
color: #67bd3c;
}
.breadcrumb > .active {
color: #67bd3c;
}
.form-info h2 em {
color: #67bd3c;
}
.nav-tabs {
border-color: #67bd3c;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #67bd3c;
}
.content-search h1 em {
color: #67bd3c;
}
.recent-work .recent-work-item:hover a.recent-work-description {
background: #67bd3c;
}
.testimonials-v1 blockquote:after {
background-color: #67bd3c;
}
.testimonials-v1 span.testimonials-name {
color: #67bd3c;
}
.search-result-item h4 a {
color: #67bd3c;
}
.top-cart-content:after {
border-bottom: 8px solid #67bd3c;
}
.goods-data a,
.checkout-page .checkout-description a {
color: #67bd3c;
}
.product-page .review a {
color: #67bd3c;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #67bd3c;
color: #fff;
}
.list-view-sorting a {
background: #fff;
color: #67bd3c;
}
::-moz-selection {
color: #fff;
background: #67bd3c;
}
::selection {
color: #fff;
background: #67bd3c;
}
.steps-block ::-moz-selection {
color: #67bd3c;
background: #fff;
}
.steps-block ::selection {
color: #67bd3c;
background: #fff;
}
.owl-buttons .owl-prev:hover {
background-color: #67bd3c;
}
.owl-buttons .owl-next:hover {
background-color: #67bd3c;
}
.steps-block-red {
background: #67bd3c;
}
.pre-footer .photo-stream img:hover {
border-color: #67bd3c;
}
.pre-footer-light dl.f-twitter dd a {
color: #67bd3c;
}
.pre-footer-light address a {
color: #67bd3c;
}
.testimonials-v1 .left-btn:hover {
background-color: #67bd3c;
}
.testimonials-v1 .right-btn:hover {
background-color: #67bd3c;
}
.blog-tags li i,
.blog-info li i {
color: #67bd3c;
}
.blog-posts .more,
.blog-sidebar .more {
color: #67bd3c;
}
.recent-news h3 a {
color: #67bd3c;
}
.blog-photo-stream li img:hover {
border-color: #67bd3c;
}
.blog-tags li a:hover {
color: #fff;
background: #67bd3c;
}
.blog-tags li a:hover:after {
border-left-color: #67bd3c;
}
.sidebar-categories li > a:hover,
.sidebar-categories li.active > a,
.sidebar-categories li.active:hover > a {
color: #67bd3c;
}
.blog-item blockquote {
border-color: #67bd3c;
}
.blog-item h4.media-heading span a {
color: #67bd3c;
}
.front-steps-wrapper .front-step1 {
background: #67bd3c;
}
.pricing-active {
border: 3px solid #67bd3c;
box-shadow: 7px 7px rgba(103, 189, 60, 0.2);
}
.pricing:hover {
border: 3px solid #67bd3c;
}
.pricing:hover h4 {
color: #67bd3c;
}
.pricing-head h3 {
background: #67bd3c;
}
.pricing-head-active h4 {
color: #67bd3c;
}
.pricing-content li i {
color: #67bd3c;
}
.top-cart-block .fa-shopping-cart {
background: #67bd3c;
}
.product-item .btn:hover {
background: #67bd3c;
}
.pi-price {
color: #67bd3c;
}
.product-item .add2cart:hover {
color: #fff !important;
background: #67bd3c !important;
border-color: #67bd3c;
}
.goods-page-price strong,
.goods-page-total strong,
.checkout-price strong,
.checkout-total strong {
color: #67bd3c;
}
.shopping-total strong,
.checkout-total-block strong {
color: #67bd3c;
}
.compare-item strong {
color: #67bd3c;
}
.sidebar-products .price {
color: #67bd3c;
}
.price-availability-block .price strong {
color: #67bd3c;
}
.require {
color: #67bd3c;
}
.content-form-page .form-control:focus {
border: solid 1px #67bd3c;
}
.content-search input:focus {
border: solid 1px #67bd3c;
}
.btn-primary {
background: #67bd3c;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
background: #67bd3c;
}
.header-navigation .dropdown-menu > li:first-child {
border-top: 2px solid #67bd3c;
}
.front-steps-wrapper .front-step1:after {
border-left: 15px solid #85CA63;
}
.del-goods:hover,
.add-goods:hover {
background-color: #85CA63;
}
.sidebar a:hover > .fa-angle-down {
background-position: -33px 0;
}
.sidebar .collapsed:hover > .fa-angle-down {
background-position: -33px -37px;
}
.top-cart-content {
border-top: solid 2px #85CA63;
}
.front-skills .progress-bar {
background: #85CA63;
}
.service-box-v1:hover {
background: #85CA63;
}
.header .mobi-toggler:hover {
background-color: #85CA63;
border-color: #85CA63;
}
@media (max-width: 1024px) {
.header .header-navigation li > a:hover,
.header .header-navigation li.active > a,
.header .header-navigation li.open > a:hover {
color: #85CA63 !important;
}
}
.faq-tabbable {
border-left: solid 2px #85CA63;
}
.faq-tabbable li:hover a,
.faq-tabbable li.active a{
background: #85CA63;
}
.faq-tabbable li.active:after {
border-left: 6px solid #85CA63;
}
.mix-filter li:hover, .mix-filter li.active {
background: #85CA63;
color: #fff;
}
.mix-grid .mix .mix-details {
background: #85CA63;
}
.mix-grid .mix a.mix-link,
.mix-grid .mix a.mix-preview {
background: #7edb1a;
}
.langs-block-others {
border-top: solid 2px #85CA63;
}
.brands .owl-buttons .owl-prev:hover {
background-position: 18px -432px;
}
.brands .owl-buttons .owl-next:hover {
background-position: -249px -432px;
}
.header-navigation ul > li.active > a/*,
.ecommerce .header-navigation ul > li.active > a*/ {
border-bottom: 2px solid #85CA63;
}
\ No newline at end of file
a {
color: #e18604;
}
a:hover {
color: #e18604;
}
.pre-header a:hover {
color: #e18604;
}
.shop-currencies a.current {
color: #e18604;
}
.header-navigation ul > li.active > a,
.header-navigation ul > li > a:hover,
.header-navigation ul > li > a:focus,
.header-navigation ul > li.open > a,
.header-navigation ul > li.open > a:hover,
.header-navigation ul > li.open > a:focus {
color: #e18604;
}
.header-navigation li.menu-search i:hover {
color: #e18604;
}
.sidebar a:hover {
color: #e18604;
}
.sidebar .dropdown.open .dropdown-toggle:hover {
color: #e18604;
}
.sidebar-menu .dropdown-menu li > a:hover, .sidebar-menu .dropdown-menu li > a:focus, .sidebar-menu li.active > a, .sidebar-menu li.active > a:hover {
color: #e18604;
}
.content-page a:hover,
.sidebar2 a:hover {
color: #e18604;
}
.content-page .link, .content-page .link:hover, .content-page .link:active {
color: #e18604;
}
.page-404 .number,
.page-500 .number {
color: #e18604;
}
.content-form-page a:hover {
color: #e18604;
}
.quote-v1 a.btn-transparent:hover {
background: #e18604;
}
.recent-work h2 a:hover {
color: #e18604;
}
.recent-work .recent-work-item .fa:hover {
color: #e18604;
}
.our-clients h2 a:hover {
color: #e18604;
}
.front-team h3 strong {
color: #e18604;
}
.ecommerce .header-navigation ul > li.active > a,
.ecommerce .header-navigation ul > li > a:hover,
.ecommerce .header-navigation ul > li > a:focus,
.ecommerce .header-navigation ul > li.open > a,
.ecommerce .header-navigation ul > li.open > a:hover,
.ecommerce .header-navigation ul > li.open > a:focus {
color: #e18604;
}
.product-item h3 a:hover {
color: #e18604;
}
.checkout-page a:hover {
color: #e18604;
}
.langs-block-others:after {
border-bottom: 8px solid #e18604;
}
.header-navigation > ul > li.dropdown:hover > a:after {
border-bottom: 8px solid #e18604;
}
.header-navigation .dropdown-menu > li > a:hover,
.header-navigation .dropdown-menu > li.active > a,
.header-navigation .header-navigation-content .header-navigation-col li > a:hover,
.header-navigation .header-navigation-content .header-navigation-col li.active > a {
background: #e18604;
color: #fff;
}
.header-navigation .dropdown-menu .header-navigation-content-ext li > a:hover,
.header-navigation .dropdown-menu .header-navigation-content-ext li.active > a {
background: #fff;
color: #e18604;
}
.header-navigation .search-box:after {
border-bottom: 8px solid #e18604;
}
.header-navigation .search-box {
border-top: solid 2px #e18604;
}
.title-wrapper h1 span {
color: #e18604;
}
.breadcrumb > .active {
color: #e18604;
}
.form-info h2 em {
color: #e18604;
}
.nav-tabs {
border-color: #e18604;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #e18604;
}
.content-search h1 em {
color: #e18604;
}
.recent-work .recent-work-item:hover a.recent-work-description {
background: #e18604;
}
.testimonials-v1 blockquote:after {
background-color: #e18604;
}
.testimonials-v1 span.testimonials-name {
color: #e18604;
}
.search-result-item h4 a {
color: #e18604;
}
.top-cart-content:after {
border-bottom: 8px solid #e18604;
}
.goods-data a,
.checkout-page .checkout-description a {
color: #e18604;
}
.product-page .review a {
color: #e18604;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #e18604;
color: #fff;
}
.list-view-sorting a {
background: #fff;
color: #e18604;
}
::-moz-selection {
color: #fff;
background: #e18604;
}
::selection {
color: #fff;
background: #e18604;
}
.steps-block ::-moz-selection {
color: #e18604;
background: #fff;
}
.steps-block ::selection {
color: #e18604;
background: #fff;
}
.owl-buttons .owl-prev:hover {
background-color: #e18604;
}
.owl-buttons .owl-next:hover {
background-color: #e18604;
}
.steps-block-red {
background: #e18604;
}
.pre-footer .photo-stream img:hover {
border-color: #e18604;
}
.pre-footer-light dl.f-twitter dd a {
color: #e18604;
}
.pre-footer-light address a {
color: #e18604;
}
.testimonials-v1 .left-btn:hover {
background-color: #e18604;
}
.testimonials-v1 .right-btn:hover {
background-color: #e18604;
}
.blog-tags li i,
.blog-info li i {
color: #e18604;
}
.blog-posts .more,
.blog-sidebar .more {
color: #e18604;
}
.recent-news h3 a {
color: #e18604;
}
.blog-photo-stream li img:hover {
border-color: #e18604;
}
.blog-tags li a:hover {
color: #fff;
background: #e18604;
}
.blog-tags li a:hover:after {
border-left-color: #e18604;
}
.sidebar-categories li > a:hover,
.sidebar-categories li.active > a,
.sidebar-categories li.active:hover > a {
color: #e18604;
}
.blog-item blockquote {
border-color: #e18604;
}
.blog-item h4.media-heading span a {
color: #e18604;
}
.front-steps-wrapper .front-step1 {
background: #e18604;
}
.pricing-active {
border: 3px solid #e18604;
box-shadow: 7px 7px rgba(225, 134, 4, 0.2);
}
.pricing:hover {
border: 3px solid #e18604;
}
.pricing:hover h4 {
color: #e18604;
}
.pricing-head h3 {
background: #e18604;
}
.pricing-head-active h4 {
color: #e18604;
}
.pricing-content li i {
color: #e18604;
}
.top-cart-block .fa-shopping-cart {
background: #e18604;
}
.product-item .btn:hover {
background: #e18604;
}
.pi-price {
color: #e18604;
}
.product-item .add2cart:hover {
color: #fff !important;
background: #e18604 !important;
border-color: #e18604;
}
.goods-page-price strong,
.goods-page-total strong,
.checkout-price strong,
.checkout-total strong {
color: #e18604;
}
.shopping-total strong,
.checkout-total-block strong {
color: #e18604;
}
.compare-item strong {
color: #e18604;
}
.sidebar-products .price {
color: #e18604;
}
.price-availability-block .price strong {
color: #e18604;
}
.require {
color: #e18604;
}
.content-form-page .form-control:focus {
border: solid 1px #e18604;
}
.content-search input:focus {
border: solid 1px #e18604;
}
.btn-primary {
background: #e18604;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
background: #e18604;
}
.header-navigation .dropdown-menu > li:first-child {
border-top: 2px solid #e18604;
}
.front-steps-wrapper .front-step1:after {
border-left: 15px solid #E79E36;
}
.del-goods:hover,
.add-goods:hover {
background-color: #E79E36;
}
.sidebar a:hover > .fa-angle-down {
background-position: -44px 0;
}
.sidebar .collapsed:hover > .fa-angle-down {
background-position: -44px -37px;
}
.top-cart-content {
border-top: solid 2px #E79E36;
}
.front-skills .progress-bar {
background: #E79E36;
}
.service-box-v1:hover {
background: #E79E36;
}
.header .mobi-toggler:hover {
background-color: #E79E36;
border-color: #E79E36;
}
@media (max-width: 1024px) {
.header .header-navigation li > a:hover,
.header .header-navigation li.active > a,
.header .header-navigation li.open > a:hover {
color: #E79E36 !important;
}
}
.faq-tabbable {
border-left: solid 2px #E79E36;
}
.faq-tabbable li:hover a,
.faq-tabbable li.active a{
background: #E79E36;
}
.faq-tabbable li.active:after {
border-left: 6px solid #E79E36;
}
.mix-filter li:hover, .mix-filter li.active {
background: #E79E36;
color: #fff;
}
.mix-grid .mix .mix-details {
background: #E79E36;
}
.mix-grid .mix a.mix-link,
.mix-grid .mix a.mix-preview {
background: #db681a;
}
.langs-block-others {
border-top: solid 2px #E79E36;
}
.brands .owl-buttons .owl-prev:hover {
background-position: 18px -539px;
}
.brands .owl-buttons .owl-next:hover {
background-position: -249px -539px;
}
.header-navigation ul > li.active > a/*,
.ecommerce .header-navigation ul > li.active > a*/ {
border-bottom: 2px solid #E79E36;
}
\ No newline at end of file
a {
color: #E02222;
}
a:hover {
color: #E02222;
}
.pre-header a:hover {
color: #E02222;
}
.shop-currencies a.current {
color: #E02222;
}
.header-navigation ul > li.active > a,
.header-navigation ul > li > a:hover,
.header-navigation ul > li > a:focus,
.header-navigation ul > li.open > a,
.header-navigation ul > li.open > a:hover,
.header-navigation ul > li.open > a:focus {
color: #e02222;
}
.header-navigation li.menu-search i:hover {
color: #e02222;
}
.sidebar a:hover {
color: #E02222;
}
.sidebar .dropdown.open .dropdown-toggle:hover {
color: #E02222;
}
.sidebar-menu .dropdown-menu li > a:hover, .sidebar-menu .dropdown-menu li > a:focus, .sidebar-menu li.active > a, .sidebar-menu li.active > a:hover {
color: #E02222;
}
.content-page a:hover,
.sidebar2 a:hover {
color: #E02222;
}
.content-page .link, .content-page .link:hover, .content-page .link:active {
color: #E02222;
}
.page-404 .number,
.page-500 .number {
color: #E02222;
}
.content-form-page a:hover {
color: #E02222;
}
.quote-v1 a.btn-transparent:hover {
background: #E02222;
}
.recent-work h2 a:hover {
color: #E02222;
}
.recent-work .recent-work-item .fa:hover {
color: #E02222;
}
.our-clients h2 a:hover {
color: #E02222;
}
.front-team h3 strong {
color: #E02222;
}
.ecommerce .header-navigation ul > li.active > a,
.ecommerce .header-navigation ul > li > a:hover,
.ecommerce .header-navigation ul > li > a:focus,
.ecommerce .header-navigation ul > li.open > a,
.ecommerce .header-navigation ul > li.open > a:hover,
.ecommerce .header-navigation ul > li.open > a:focus {
color: #e02222;
}
.product-item h3 a:hover {
color: #E02222;
}
.checkout-page a:hover {
color: #E02222;
}
.langs-block-others:after {
border-bottom: 8px solid #e6400c;
}
.header-navigation > ul > li.dropdown:hover > a:after {
border-bottom: 8px solid #e6400c;
}
.header-navigation .dropdown-menu > li > a:hover,
.header-navigation .dropdown-menu > li.active > a,
.header-navigation .header-navigation-content .header-navigation-col li > a:hover,
.header-navigation .header-navigation-content .header-navigation-col li.active > a {
background: #e6400c;
color: #fff;
}
.header-navigation .dropdown-menu .header-navigation-content-ext li > a:hover,
.header-navigation .dropdown-menu .header-navigation-content-ext li.active > a {
background: #fff;
color: #e6400c;
}
.header-navigation .search-box:after {
border-bottom: 8px solid #e6400c;
}
.header-navigation .search-box {
border-top: solid 2px #ea4c1d;
}
.title-wrapper h1 span {
color: #e6400c;
}
.breadcrumb > .active {
color: #e6400c;
}
.form-info h2 em {
color: #e6400c;
}
.nav-tabs {
border-color: #e6400c;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #e6400c;
}
.content-search h1 em {
color: #e6400c;
}
.recent-work .recent-work-item:hover a.recent-work-description {
background: #E6400C;
}
.testimonials-v1 blockquote:after {
background-color: #E6400C;
}
.testimonials-v1 span.testimonials-name {
color: #E6400C;
}
.search-result-item h4 a {
color: #E6400C;
}
.top-cart-content:after {
border-bottom: 8px solid #e6400c;
}
.goods-data a,
.checkout-page .checkout-description a {
color: #e6400c;
}
.product-page .review a {
color: #e6400c;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
background: #E6400C;
color: #fff;
}
.list-view-sorting a {
background: #fff;
color: #E6400C;
}
::-moz-selection {
color: #fff;
background: #e45000;
}
::selection {
color: #fff;
background: #e45000;
}
.steps-block ::-moz-selection {
color: #e45000;
background: #fff;
}
.steps-block ::selection {
color: #e45000;
background: #fff;
}
.owl-buttons .owl-prev:hover {
background-color: #e84d1c;
}
.owl-buttons .owl-next:hover {
background-color: #e84d1c;
}
.steps-block-red {
background: #e84d1c;
}
.pre-footer .photo-stream img:hover {
border-color: #E84D1C;
}
.pre-footer-light dl.f-twitter dd a {
color: #e84d1c;
}
.pre-footer-light address a {
color: #e84d1c;
}
.testimonials-v1 .left-btn:hover {
background-color: #e84d1c;
}
.testimonials-v1 .right-btn:hover {
background-color: #e84d1c;
}
.blog-tags li i,
.blog-info li i {
color: #E84D1C;
}
.blog-posts .more,
.blog-sidebar .more {
color: #E84D1C;
}
.recent-news h3 a {
color: #E84D1C;
}
.blog-photo-stream li img:hover {
border-color: #E84D1C;
}
.blog-tags li a:hover {
color: #fff;
background: #E84D1C;
}
.blog-tags li a:hover:after {
border-left-color: #E84D1C;
}
.sidebar-categories li > a:hover,
.sidebar-categories li.active > a,
.sidebar-categories li.active:hover > a {
color: #E84D1C;
}
.blog-item blockquote {
border-color: #E84D1C;
}
.blog-item h4.media-heading span a {
color: #E84D1C;
}
.front-steps-wrapper .front-step1 {
background: #E84D1C;
}
.pricing-active {
border: 3px solid #E84D1C;
box-shadow: 7px 7px rgba(232, 77, 22, 0.2);
}
.pricing:hover {
border: 3px solid #E84D1C;
}
.pricing:hover h4 {
color: #E84D1C;
}
.pricing-head h3 {
background: #E84D1C;
}
.pricing-head-active h4 {
color: #E84D1C;
}
.pricing-content li i {
color: #E84D1C;
}
.top-cart-block .fa-shopping-cart {
background: #e84d1c;
}
.product-item .btn:hover {
background: #e84d1c;
}
.pi-price {
color: #e84d1c;
}
.product-item .add2cart:hover {
color: #fff !important;
background: #E84D1C !important;
border-color: #E84D1C;
}
.goods-page-price strong,
.goods-page-total strong,
.checkout-price strong,
.checkout-total strong {
color: #e84d1c;
}
.shopping-total strong,
.checkout-total-block strong {
color: #e84d1c;
}
.compare-item strong {
color: #E84D1C;
}
.sidebar-products .price {
color: #E84D1C;
}
.price-availability-block .price strong {
color: #e84d1c;
}
.require {
color: #e94d1c;
}
.content-form-page .form-control:focus {
border: solid 1px #e94d1c;
}
.content-search input:focus {
border: solid 1px #e94d1c;
}
.btn-primary {
background: #e94d1c;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
background: #cc3304;
}
.header-navigation .dropdown-menu > li:first-child {
border-top: 2px solid #EA4C1D;
}
.front-steps-wrapper .front-step1:after {
border-left: 15px solid #EC7049;
}
.del-goods:hover,
.add-goods:hover {
background-color: #E94D1C;
}
.sidebar a:hover > .fa-angle-down {
background-position: -11px 0;
}
.sidebar .collapsed:hover > .fa-angle-down {
background-position: -11px -37px;
}
.top-cart-content {
border-top: solid 2px #ea4c1d;
}
.front-skills .progress-bar {
background: #EF4D2E;
}
.service-box-v1:hover {
background: #d73d04;
}
.header .mobi-toggler:hover {
background-color: #e34f00;
border-color: #e34f00;
}
@media (max-width: 1024px) {
.header .header-navigation li > a:hover,
.header .header-navigation li.active > a,
.header .header-navigation li.open > a:hover {
color: #dd4632 !important;
}
}
.faq-tabbable {
border-left: solid 2px #e44f00;
}
.faq-tabbable li:hover a,
.faq-tabbable li.active a{
background: #e44f00;
}
.faq-tabbable li.active:after {
border-left: 6px solid #e44f00;
}
.mix-filter li:hover, .mix-filter li.active {
background: #e44f00;
color: #fff;
}
.mix-grid .mix .mix-details {
background: #e44f00;
}
.mix-grid .mix a.mix-link,
.mix-grid .mix a.mix-preview {
background: #DB3A1B;
}
.langs-block-others {
border-top: solid 2px #ea4c1d;
}
.brands .owl-buttons .owl-prev:hover {
background-position: 18px -217px;
}
.brands .owl-buttons .owl-next:hover {
background-position: -249px -217px;
}
.header-navigation ul > li.active > a/*,
.ecommerce .header-navigation ul > li.active > a*/ {
border-bottom: 2px solid #e64f00;
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment