Now you can save to files (Not that I'd recommend doing that, but whatever), and it's easier on the eyes to read.

Also, your saving and reading will not work, you should be doing
self:SteamIDFormat() instead of SteamIDFormat since it's a function and not a variable.

A quick fix instead of redoing all that will be putting

local SteamIDFormat = self:SteamIDFormat()

at the top of the file underneath the money variable.

That should fix the erroring but in case you haven't noticed your GetMoney function will only work serverside.

Get rid of the function arguments in ply_meta:GetMoney()
Also replace GetPData() with GetNWInt() because GetPData() is only allowed to be called through the server. NW isn't. However the SetNW's and GetNW's can be expensive. They should only be called when you need to give it to all players. Don't get overexcited about NW's, spamming them everywhere for a shortcut. Your FPS will pay for that.

Now, the function should now look something like this now:

function ply_meta:GetMoney()
-- Return money using GetNWInt instead so you may use it in HUD's or menus.
return self:GetNWInt( money )
end

There shouldn't be an error in ply_meta:HasMoney(), but usually coders will not put an error like "You do not have enough money" in a meta function, and save it for a function that actually calls it and stuff, because you do not know 100% what you will use HasMoney() for.
Not only that, but since you are making a money system serverside you would be printing "You do not have enough money" to the server console, and not the player himself.

If you decide to keep the ply_meta:HasMoney( amount ) then at least do something like this:

function ply_meta:HasMoney( amount )
if self:GetPData( money ) >= amount then
-- Don't take their money!
-- You do NOT know what you're going to use this for 100%
-- Return true to say that the player has the money needed.
return true
else
-- Return false: Player doesn't have the money needed :(
return false
end
end

Couple more things:

Your InitialPlayerSpawn( ply ) function, good, it's local and it should be unless you plan on calling it somewhere else? (You won't)
If you plan on making a HUD or menu, switch that
ply:SetPData( money, file.Read("money_system_test/" .. SteamIDFormat .. ".txt") )
with
ply:SetNWInt( money, file.Read("money_system_test/" .. SteamIDFormat .. ".txt") )

A big thing too:
When you are creating a timer, the timer is being set for everyone, so if a new player comes the player that was about to get a payday gets the timer reset, or something like that will happen.

Change it to

timer.Create( "PayDayBitches_"..ply:UserID(), time, 0, function()

Now the saving timer, how often do you plan on setting players payday timer?
If it's plan is 60 seconds I would still make it save like every 30 seconds. ( You never now if someone will trade money or spend something or etc)

ALSO 2 more things

1.) I recommend that you add a playerdisconnect save money hook. Something like this: